diff -Nru chntpw-0.99.6/COPYING.txt chntpw-0.99.6.110511/COPYING.txt --- chntpw-0.99.6/COPYING.txt 1970-01-01 00:00:00.000000000 +0000 +++ chntpw-0.99.6.110511/COPYING.txt 2011-05-11 19:33:56.000000000 +0000 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; either version 2 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -Nru chntpw-0.99.6/HISTORY.txt chntpw-0.99.6.110511/HISTORY.txt --- chntpw-0.99.6/HISTORY.txt 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/HISTORY.txt 2011-05-11 19:33:56.000000000 +0000 @@ -1,7 +1,7 @@ The Offline NT Password Editor -(c) 1997-2008 Petter Nordahl-Hagen +(c) 1997-2011 Petter Nordahl-Hagen See README for general info, copyright & credits. See INSTALL for compile/installation instructions. @@ -9,6 +9,97 @@ Revision history (tools only, see website for info on bootdisk revisions) +* v 0.99.6 110511 + +regedit library (chntpw and reged uses it): +- Hive expansion! Library now does hive file expansion by + default! (but you can turn it off, safe mode). If expansion occured, you + will get a warning when saving the hive. +- There were a lot of goofs by me in the add/delete key and value + handling, which made windows complain and lose data. Hope I managed + to fix them. +- Also, found out more about how Windows does things, for example: + - Empty name (default) value, flag field is 0 (not 1 as usual) + - Values with data size 0 to 4 the value data itself is stored in + the data pointer field, typical for DWORD (this I knew a long time + ago), sign bit of data size field is then set. BUT.. + - In a lot of cases in SAM, for null name values, size is 0 with sign + set. In that case, the data dword is stored in the TYPE field, + and the rest is unused. This saves a few bytes of course.. but why? + The rest of the stuff registry is used for is often seriously bloated + anyway. Did the guys at MS who actually wrote the SAM stuff back + in the NT3 days actually do it because they cared? or just because + they found something new and exiting they smoked? :) + - Large values (seems to be from around 16k) are split internally in + several parts. Sounds smart. But it goes via 2 (two) separate data + structures that has to be allocated. + For regular small values the data pointer simply points to the data + itself. For this split large value it points to a "db" struct. This + contais a count of how many parts there are. And then the list of + pointers to the data parts? Nope. It points to another area where + the list of pointers are stored. + Pseudo: vk.ofs_data -> db.list -> list[n] -> raw data block n + The last raw data block always allocates the whole hbin (usually + 16k) it is in, even if all of it is not needed. Of course only the correct + amount of data is copied out based on the value length. + On new Vista64 bit SP2, this happens at least 2 times in + SOFTWARE\Microsoft tree. +reged: +- On popular demand: .reg file import!! (-I) +- Will read files from regedit.exe in most cases + (UTF-16) but can miss on some international characters. Also reads + "latin" (8 bit) files. +- Only one .reg and hive at a time supported. +- Did quite a lot of testing by importing for example the + SOFTWARE\Classes tree with regedit.exe and importing it again into + the DEFAULT hive with my tool, then ask windows to "Load hive" (in + regedit.exe). If it does not complain in the event log, it is good. + (Also, seems like windows has gotten better at not bluescreening on + a corrupt registry hive, did not get a single one when doing this + with pretty messed up hives in win7 and Vista) +- Be aware that .reg import is currently very slow, since I messed + up the design for the string reading badly, and also the add routines is + not exactly optimized. Consider it a proof of concept! Example: + Import of SOFTWARE\Microsoft tree (exported by regedit.exe) into the + small DEFAULT hive took more than 10 minutes on a pretty fast + machine. Especially hex data is slow (one byte at a time.. lazy me..) +- WARNING: .reg file import does not do much sanity checking of the + input .reg file. It will either crash during import or mess up + the registry if the .reg file is bad. +- WARNING2: Limitation: Be careful when importing keys that has large + number of subkeys (like several 1000) since it does not split up + into indirect indexes (lh) yet, and windows may not like it. +- -N and -E options for safe mode edit (no alloc and no expand hive) +- Importing (-I) and then into edit (-e) before save possible, + by specifying both options. +- -I and -C (-IC) will import and auto-save, use this in scripts. +chntpw: +- This version has no significant changes in the password (reset) + handling part of the tools. +TODO list: +- Windows like API. Faster .reg import. Fix bugs! Maybe not in that +order :) + + +* v 0.99.6 100627 + chntpw: + - Syskey not visible in menu anymore, but is still selectable as # 2 + This because too many people just went ahead without understanding + its purpose, and the emailed me when things went as expected, that + is it went *boom* + - Interactive menu adapts to show most relevant selections based + on what is loaded + reged: + - Patches from Frediano Ziglio adding or fixing: + buffer overflow in export_subkey printing keyname + some quoting error (name and string values must be quoted) + missing support for wide character encoding in keys and value names + regedit library (chntpw and reged uses it): + - New function from Aleksander Wojdyga: dpi, to decode product IDs + Can be used on for example \Microsoft\Windows NT\CurrentVersion\DigitalProductId + to find the systems product ID in cleartext. + Now as command in registry editor, but may be moved to chnpw menu later. + * v 0.99.6 080526 reged: - NEW TOOL: It's actually just the interactive registry edit diff -Nru chntpw-0.99.6/INSTALL.txt chntpw-0.99.6.110511/INSTALL.txt --- chntpw-0.99.6/INSTALL.txt 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/INSTALL.txt 2011-05-11 19:33:56.000000000 +0000 @@ -1,7 +1,7 @@ The Offline NT Password & Registry Editor -(c) 1997-2008 Petter Nordahl-Hagen +(c) 1997-2011 Petter Nordahl-Hagen This file is meant for developers. @@ -36,7 +36,7 @@ reged - Registry edit tool, dynamic linked with libc. No crypto. reged.static - Registry too, statically linked. cpnt - Simple copy utility, does not truncate file on write - (was at least needed earlier for NTFS writes) + Now deprecated, was at least needed earlier for NTFS writes. Also, the floppies and CDs are build under a different environment for small libc size (uClibc), see website diff -Nru chntpw-0.99.6/Makefile chntpw-0.99.6.110511/Makefile --- chntpw-0.99.6/Makefile 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/Makefile 2011-05-11 19:33:56.000000000 +0000 @@ -13,7 +13,7 @@ CC=gcc # Force 32 bit -CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall -m32 +CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall -m32 OSSLLIB=$(OSSLPATH)/lib # 64 bit if default for compiler setup diff -Nru chntpw-0.99.6/README.txt chntpw-0.99.6.110511/README.txt --- chntpw-0.99.6/README.txt 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/README.txt 2011-05-11 19:33:56.000000000 +0000 @@ -1,7 +1,7 @@ The Offline NT Password Editor -(c) 1997-2008 Petter Nordahl-Hagen +(c) 1997-2011 Petter Nordahl-Hagen This is free software, licensed under the following: @@ -19,7 +19,7 @@ Where to get more info: ----------------------- -http://home.eunet.no/~pnordahl/ntpasswd/ +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. @@ -29,13 +29,21 @@ -------------------- This little program will enable you to view some information and -change user passwords in a Windows NT SAM userdatabase file. +change user passwords in a Windows (NT/XP/Vista/win7) 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. 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. +Also have registry import or export +----------------------------------- + +"reged" is a program that can do import and export of .reg files into +the registry hive (binary) files. Also has an editor, but still +rudimentary text based command line type thing. + + Why? ---- @@ -57,7 +65,7 @@ So, to set a new adminpassword on your NT 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://home.eunet.no/~pnordahl/ntpasswd/ + one is available at: http://pogostick.net/~pnh/ntpasswd/ ie. you do it offline, with the NT system down. Usage: diff -Nru chntpw-0.99.6/chntpw.c chntpw-0.99.6.110511/chntpw.c --- chntpw-0.99.6/chntpw.c 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/chntpw.c 2011-05-11 19:33:56.000000000 +0000 @@ -1,10 +1,14 @@ /* - * chntpw.c - Offline Password Edit Utility for NT 3.51 4.0 5.0 5.1 6.0 SAM database. + * chntpw.c - Offline Password Edit Utility for Windows SAM database * - * This program uses the "ntreg" library to load and access the registry, it's purpose - * is to reset password based information. - * There is also a simple commandline based registry editor included. - * + * This program uses the "ntreg" library to load and access the registry, + * it's main purpose is to reset password based information. + * It can also call the registry editor etc + + * 2011-apr: Command line options added for hive expansion safe mode + * 2010-jun: Syskey not visible in menu, but is selectable (2) + * 2010-apr: Interactive menu adapts to show most relevant + * selections based on what is loaded * 2008-mar: Minor other tweaks * 2008-mar: Interactive reg ed moved out of this file, into edlib.c * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev @@ -29,7 +33,7 @@ * ***** * - * Copyright (c) 1997-2008 Petter Nordahl-Hagen. + * Copyright (c) 1997-2011 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 @@ -71,7 +75,7 @@ #include "ntreg.h" #include "sam.h" -const char chntpw_version[] = "chntpw version 0.99.6 080526 (sixtyfour), (c) Petter N Hagen"; +const char chntpw_version[] = "chntpw version 0.99.6 110511 , (c) Petter N Hagen"; extern char *val_types[REG_MAX+1]; @@ -792,11 +796,9 @@ /* Extract the value out of the username-key, value is RID */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",ex.name); - rid = get_dword(hive[H_SAM], 0, s, TPF_VK_EXACT); + rid = get_dword(hive[H_SAM], 0, s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == 500) strncpy(admuser,ex.name,128); /* Copy out admin-name */ - /* printf("name: %s, rid: %d (0x%0x)\n", ex.name, rid, rid); */ - /* Now that we have the RID, build the path to, and get the V-value */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid); v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); @@ -825,7 +827,6 @@ void find_n_change(char *username) { char s[200]; - struct vk_key *vkkey; struct keyval *v; int rid = 0; @@ -835,7 +836,7 @@ 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\\@",username); - rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT); + rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT); if (rid == -1) { printf("Cannot find value <%s>\n",s); return; @@ -856,7 +857,7 @@ if (v->len < 0xcc) { printf("Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", - s, vkkey->len_data); + s, v->len); } else { change_pw( (char *)&v->data , rid, v->len, 0); if (dirty) { @@ -933,7 +934,7 @@ secboot = -1; if (H_SYS >= 0) { - secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT); + secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT ); } secmode = -1; @@ -992,11 +993,11 @@ printf("\n** IF YOU DON'T KNOW WHAT SYSKEY IS YOU DO NOT NEED TO SWITCH IT OFF!**\n"); printf("NOTE: On WINDOWS 2000 and XP it will not be possible\n"); printf("to turn it on again! (and other problems may also show..)\n\n"); - printf("EXTREME WARNING: Do not try this on Vista, it will go into endless re-boots\n\n"); printf("NOTE: Disabling syskey will invalidate ALL\n"); printf("passwords, requiring them to be reset. You should at least reset the\n"); printf("administrator password using this program, then the rest ought to be\n"); printf("done from NT.\n"); + printf("\nEXTREME WARNING: Do not try this on Vista or Win 7, it will go into endless re-boots\n\n"); fmyinput("\nDo you really wish to disable SYSKEY? (y/n) [n] ",yn,2); if (*yn == 'y') { @@ -1098,7 +1099,7 @@ sec = get_dword(hive[H_SOF],0,slpath,TPF_VK_EXACT); if (cmd == -1 && sec == -1) { - printf("\nDid not find registry entries for RecoveryConsole.\n(RecoveryConsole is only in Windows 2000 and XP, not Vista)\n"); + printf("\nDid not find registry entries for RecoveryConsole.\n(RecoveryConsole is only in Windows 2000 and XP)\n"); return; } @@ -1130,10 +1131,25 @@ for (il = 0; il < no_hives; il++) { printf(" <%s>",hive[il]->filename); } - printf("\n\n 1 - Edit user data and passwords\n" - " 2 - Syskey status & change\n" - " 3 - RecoveryConsole settings\n" - " - - -\n" + + printf("\n\n"); + + /* Make menu selection depending on what is loaded + but it is still possible to select even if not shown */ + + if (H_SAM >= 0) printf(" 1 - Edit user data and passwords\n"); + +#if 0 + if (H_SAM >= 0 && H_SYS >= 0 && H_SEC >= 0) { + printf(" 2 - Syskey status & change\n"); + } +#endif + if (H_SOF >= 0) { + printf(" 3 - RecoveryConsole settings\n"); + printf(" 4 - Show product key (DigitalProductID)\n"); + } + + printf(" - - -\n" " 9 - Registry editor, now with full write support!\n" " q - Quit (you will be asked if there is something to save)\n" "\n\n"); @@ -1146,6 +1162,7 @@ case '1': useredit(); break; case '2': handle_syskey(); break; case '3': recoveryconsole(); break; + case '4': cat_dpi(hive[H_SOF],0,"\\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId"); break; case '9': mainloop(); break; case 'q': return; break; } @@ -1156,7 +1173,9 @@ void usage(void) { - printf("chntpw: change password of a user in a NT/2k/XP/2k3/Vista SAM file, or invoke registry editor.\n" + printf("chntpw: change password of a user in a Windows SAM file,\n" + "or invoke registry editor. Should handle both 32 and 64 bit windows and\n" + "all version from NT3.x to Win7\n" "chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...]\n" " -h This message\n" " -u Username to change, Administrator is default\n" @@ -1164,10 +1183,10 @@ " -i Interactive. List users (as -l) then ask for username to change\n" " -e Registry editor. Now with full write support!\n" " -d Enter buffer debugger instead (hex editor), \n" - " -t Trace. Show hexdump of structs/segments. (deprecated debug function)\n" " -v Be a little more verbose (for debuging)\n" - " -L Write names of changed files to /tmp/changed\n" - " -N No allocation mode. Only (old style) same length overwrites possible\n" + " -L For scripts, write names of changed files to /tmp/changed\n" + " -N No allocation mode. Only same length overwrites possible (very safe mode)\n" + " -E No expand mode, do not expand hive file (safe mode)\n" "See readme file on how to get to the registry files, and what they are.\n" "Source/binary freely distributable under GPL v2 license. See README for details.\n" "NOTE: This program is somewhat hackish! You are on your own!\n" @@ -1185,7 +1204,7 @@ char iwho[100]; FILE *ch; /* Write out names of touched files to this */ - char *options = "LNidehltvu:"; + char *options = "LENidehlvu:"; printf("%s\n",chntpw_version); while((c=getopt(argc,argv,options)) > 0) { @@ -1194,8 +1213,8 @@ case 'e': edit = 1; break; case 'L': logchange = 1; break; case 'N': mode |= HMODE_NOALLOC; break; + case 'E': mode |= HMODE_NOEXPAND; break; case 'l': list = 1; who = 0; break; - case 't': list = 3; who = 0; mode |= HMODE_TRACE; break; case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break; case 'i': list = 2; who = 0; inter = 1; break; case 'u': who = optarg; list = 2; break; @@ -1240,7 +1259,10 @@ printf("\nHives that have changed:\n # Name\n"); for (il = 0; il < no_hives; il++) { if (hive[il]->state & HMODE_DIRTY) { - if (!logchange) printf("%2d <%s>\n",il,hive[il]->filename); + if (!logchange) printf("%2d <%s>",il,hive[il]->filename); + if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n"); + printf("\n"); + d = 1; } } @@ -1256,7 +1278,9 @@ if (hive[il]->state & HMODE_DIRTY) { printf("%2d <%s> - ",il,hive[il]->filename); if (!writeHive(hive[il])) { - printf("OK\n"); + printf("OK"); + if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n"); + printf("\n"); if (logchange) fprintf(ch,"%s ",hive[il]->filename); dd = 2; } Binary files /tmp/SX_uc0o7KC/chntpw-0.99.6/chntpw.static and /tmp/BqcNAvCQc1/chntpw-0.99.6.110511/chntpw.static differ diff -Nru chntpw-0.99.6/debian/changelog chntpw-0.99.6.110511/debian/changelog --- chntpw-0.99.6/debian/changelog 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/changelog 2014-01-30 05:54:15.000000000 +0000 @@ -1,3 +1,20 @@ +chntpw (0.99.6.110511-1) unstable; urgency=medium + + * Update to latest upstream release, publised in 2011-05-09 (Closes: #615965) + * Change Build-Dep to use 'libgcrypt11-dev | libgcrypt-dev' instead of + 'libgcrypt11-dev | libssl-dev' (Closes: #639350) + * debian/patches: + - Refreshed patches + - Remove patches that do not apply anymore, some of these + were incorporated upstream. + - Fix 09_improve_robustness, the length of the input call was + not being properly calculated, resulting in chntpwd not clearing + the password properly. This was fixed in Fedora's patches see: + http://pkgs.fedoraproject.org/cgit/chntpw.git/commit/?id=13bf89e14642a0da681384de5b6360178c3f8d57 + (Closes: #705292) + + -- Javier Fernández-Sanguino Peña Wed, 29 Jan 2014 19:47:17 +0100 + chntpw (0.99.6-2) unstable; urgency=low * Add patches provided by Fedora to fix regex -x crahses, see diff -Nru chntpw-0.99.6/debian/control chntpw-0.99.6.110511/debian/control --- chntpw-0.99.6/debian/control 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/control 2014-01-30 05:54:15.000000000 +0000 @@ -2,7 +2,7 @@ Section: admin Priority: optional Maintainer: Javier Fernandez-Sanguino Pen~a -Build-Depends: debhelper (>> 3.0.0), libgcrypt11-dev | libssl-dev, sharutils, quilt +Build-Depends: debhelper (>> 3.0.0), libgcrypt11-dev | libgcrypt-dev, sharutils, quilt Standards-Version: 3.8.4 Homepage: http://pogostick.net/~pnh/ntpasswd/ diff -Nru chntpw-0.99.6/debian/patches/01_port_to_gcrypt.patch chntpw-0.99.6.110511/debian/patches/01_port_to_gcrypt.patch --- chntpw-0.99.6/debian/patches/01_port_to_gcrypt.patch 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/01_port_to_gcrypt.patch 2014-01-30 05:54:15.000000000 +0000 @@ -5,15 +5,12 @@ Forwarded to Petter Nordahl-Hagen Updated by Philippe Coval for debian -diff --git a/Makefile b/Makefile -index c3e06c5..8a6f6fb 100644 --- a/Makefile +++ b/Makefile -@@ -1,37 +1,24 @@ - # +@@ -2,45 +2,26 @@ # Makefile for the Offline NT Password Editor # --# + # -# Change here to point to the needed OpenSSL libraries & .h files -# See INSTALL for more info. -# @@ -24,22 +21,22 @@ CC=gcc - # Force 32 bit --CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall -m32 +-# Force 32 bit +-CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall -m32 +CFLAGS= -DUSELIBGCRYPT -I. $(shell libgcrypt-config --cflags) -Wall -m32 OSSLLIB=$(OSSLPATH)/lib - # 64 bit if default for compiler setup - #CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall - #OSSLLIB=$(OSSLPATH)/lib64 - -+LIBS=$(shell libgcrypt-config --libs) +-# 64 bit if default for compiler setup +-#CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall +-#OSSLLIB=$(OSSLPATH)/lib64 +- -# This is to link with whatever we have, SSL crypto lib we put in static -LIBS=-L$(OSSLLIB) $(OSSLLIB)/libcrypto.a -- ++LIBS=$(shell libgcrypt-config --libs) + -all: chntpw chntpw.static cpnt reged reged.static -+all: chntpw cpnt reged ++all: chntpw cpnt reged chntpw: chntpw.o ntreg.o edlib.o $(CC) $(CFLAGS) -o chntpw chntpw.o ntreg.o edlib.o $(LIBS) @@ -50,19 +47,33 @@ cpnt: cpnt.o $(CC) $(CFLAGS) -o cpnt cpnt.o $(LIBS) -diff --git a/chntpw.c b/chntpw.c -index 35202cc..09e548f 100644 + reged: reged.o ntreg.o edlib.o + $(CC) $(CFLAGS) -o 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 +- + + #ts: ts.o ntreg.o + # $(CC) $(CFLAGS) -nostdlib -o ts ts.o ntreg.o $(LIBS) +@@ -51,5 +32,5 @@ + $(CC) -c $(CFLAGS) $< + + clean: +- rm -f *.o chntpw chntpw.static cpnt reged reged.static *~ ++ -rm -f *.o chntpw chntpw.static cpnt reged reged.static *~ + --- a/chntpw.c +++ b/chntpw.c -@@ -5,6 +5,7 @@ - * is to reset password based information. - * There is also a simple commandline based registry editor included. - * -+ * 2008-may: port to libgcrypt to avoid GPL/OpenSSL incompatibility +@@ -9,6 +9,7 @@ + * 2010-jun: Syskey not visible in menu, but is selectable (2) + * 2010-apr: Interactive menu adapts to show most relevant + * selections based on what is loaded ++ * 2008-may: port to libgcrypt to avoid GPL/OpenSSL incompatibility [Debian] * 2008-mar: Minor other tweaks * 2008-mar: Interactive reg ed moved out of this file, into edlib.c * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev -@@ -61,12 +62,19 @@ +@@ -65,12 +66,19 @@ #include #include @@ -83,7 +94,7 @@ #include "ntreg.h" #include "sam.h" -@@ -138,7 +146,9 @@ void str_to_key(unsigned char *str,unsigned char *key) +@@ -142,7 +150,9 @@ for (i=0;i<8;i++) { key[i] = (key[i]<<1); } @@ -93,7 +104,7 @@ } /* -@@ -183,6 +193,7 @@ void sid_to_key2(uint32_t sid,unsigned char deskey[8]) +@@ -187,6 +197,7 @@ void E1(uchar *k, uchar *d, uchar *out) { @@ -101,7 +112,7 @@ des_key_schedule ks; des_cblock deskey; -@@ -193,6 +204,15 @@ void E1(uchar *k, uchar *d, uchar *out) +@@ -197,6 +208,15 @@ des_set_key((des_cblock *)deskey,ks); #endif /* __FreeBsd__ */ des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT); @@ -117,7 +128,7 @@ } -@@ -500,10 +520,18 @@ char *change_pw(char *buf, int rid, int vlen, int stat) +@@ -504,10 +524,18 @@ int dontchange = 0; struct user_V *v; @@ -136,7 +147,7 @@ unsigned char digest[16]; unsigned short acb; -@@ -617,6 +645,7 @@ char *change_pw(char *buf, int rid, int vlen, int stat) +@@ -621,6 +649,7 @@ hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16); } @@ -144,7 +155,7 @@ /* Get the two decrpt keys. */ sid_to_key1(rid,(unsigned char *)deskey1); des_set_key((des_cblock *)deskey1,ks1); -@@ -634,6 +663,25 @@ char *change_pw(char *buf, int rid, int vlen, int stat) +@@ -638,6 +667,25 @@ (des_cblock *)lanman, ks1, DES_DECRYPT); des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8), (des_cblock *)&lanman[8], ks2, DES_DECRYPT); @@ -170,7 +181,7 @@ if (gverbose) { hexprnt("MD4 hash : ",(unsigned char *)md4,16); -@@ -689,9 +737,17 @@ char *change_pw(char *buf, int rid, int vlen, int stat) +@@ -693,9 +741,17 @@ /* printf("Ucase Lanman: %s\n",newlanpw); */ @@ -188,7 +199,7 @@ if (gverbose) hexprnt("\nNEW MD4 hash : ",digest,16); -@@ -700,6 +756,7 @@ char *change_pw(char *buf, int rid, int vlen, int stat) +@@ -704,6 +760,7 @@ if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16); @@ -196,7 +207,7 @@ /* Encrypt the NT md4 password hash as two 8 byte blocks. */ des_ecb_encrypt((des_cblock *)digest, (des_cblock *)despw, ks1, DES_ENCRYPT); -@@ -710,6 +767,18 @@ char *change_pw(char *buf, int rid, int vlen, int stat) +@@ -714,6 +771,18 @@ (des_cblock *)newlandes, ks1, DES_ENCRYPT); des_ecb_encrypt((des_cblock *)(lanman+8), (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT); diff -Nru chntpw-0.99.6/debian/patches/04_get_abs_path chntpw-0.99.6.110511/debian/patches/04_get_abs_path --- chntpw-0.99.6/debian/patches/04_get_abs_path 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/04_get_abs_path 2014-01-30 05:54:15.000000000 +0000 @@ -1,28 +1,10 @@ --- a/ntreg.c +++ b/ntreg.c -@@ -1193,7 +1193,7 @@ - { - /* int newnkofs; */ - struct nk_key *key; -- char tmp[ABSPATHLEN+1]; -+ char tmp[ABSPATHLEN]; - - maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN); - -@@ -1209,6 +1209,7 @@ +@@ -1436,6 +1436,7 @@ } strncpy(tmp,path,ABSPATHLEN-1); + tmp[ABSPATHLEN-1] = '\0'; - if ( (strlen(path) + key->len_name) >= maxlen-6) { - snprintf(path,maxlen,"(...)%s",tmp); -@@ -1216,7 +1217,7 @@ - } - *path = '\\'; - memcpy(path+1,key->keyname,key->len_name); -- strncpy(path+key->len_name+1,tmp,maxlen); -+ strcpy(path+key->len_name+1,tmp); - return(get_abs_path(hdesc, key->ofs_parent+0x1004, path, maxlen)); /* go back one more */ - } - + if (key->type & 0x20) + keyname = mem_str(key->keyname, key->len_name); diff -Nru chntpw-0.99.6/debian/patches/06_correct_test_open_syscall chntpw-0.99.6.110511/debian/patches/06_correct_test_open_syscall --- chntpw-0.99.6/debian/patches/06_correct_test_open_syscall 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/06_correct_test_open_syscall 2014-01-30 05:54:15.000000000 +0000 @@ -5,7 +5,7 @@ --- a/ntreg.c +++ b/ntreg.c -@@ -2760,7 +2760,7 @@ +@@ -4029,7 +4029,7 @@ if ( !(hdesc->state & HMODE_DIRTY)) return(0); if ( !(hdesc->state & HMODE_OPEN)) { /* File has been closed */ diff -Nru chntpw-0.99.6/debian/patches/07_detect_failure_to_write_key chntpw-0.99.6.110511/debian/patches/07_detect_failure_to_write_key --- chntpw-0.99.6/debian/patches/07_detect_failure_to_write_key 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/07_detect_failure_to_write_key 2014-01-30 05:54:15.000000000 +0000 @@ -1,9 +1,9 @@ Detect stream write failure. --- a/ntreg.c +++ b/ntreg.c -@@ -2730,7 +2730,14 @@ - fprintf(file, "Windows Registry Editor Version 5.00\r\n\r\n"); - export_subkey(hdesc, nkofs, name, prefix, file); +@@ -3390,7 +3390,14 @@ + + fprintf(file,"\r\n"); /* Must end file with an empty line, windows does that */ - fclose(file); + if (ferror (file)) { @@ -16,4 +16,4 @@ + strerror(errno)); } - + /* ================================================================ */ diff -Nru chntpw-0.99.6/debian/patches/08_no_deref_null chntpw-0.99.6.110511/debian/patches/08_no_deref_null --- chntpw-0.99.6/debian/patches/08_no_deref_null 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/08_no_deref_null 2014-01-30 05:54:15.000000000 +0000 @@ -1,8 +1,8 @@ Diagnose a missing hive file name with -e. --- a/reged.c +++ b/reged.c -@@ -99,6 +99,11 @@ - } +@@ -167,6 +167,11 @@ + if (edit) { /* Call editor. Rest of arguments are considered hives to load */ hivename = argv[optind+no_hives]; + if (!hivename) { diff -Nru chntpw-0.99.6/debian/patches/09_improve_robustness chntpw-0.99.6.110511/debian/patches/09_improve_robustness --- chntpw-0.99.6/debian/patches/09_improve_robustness 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/09_improve_robustness 2014-01-30 05:54:15.000000000 +0000 @@ -11,7 +11,7 @@ * ntreg.c (openHive): Don't read uninitialized when file is too small. --- a/ntreg.c +++ b/ntreg.c -@@ -82,14 +82,16 @@ +@@ -190,14 +190,18 @@ int fmyinput(char *prmpt, char *ibuf, int maxlen) { @@ -23,27 +23,18 @@ + len = strlen(ibuf); - ibuf[strlen(ibuf)-1] = 0; -+ if (len) -+ ibuf[len-1] = 0; - +- - return(strlen(ibuf)); ++ if (len) { ++ ibuf[len-1] = 0; ++ --len; ++ } ++ + return len; } /* Print len number of hexbytes */ -@@ -2583,7 +2585,10 @@ - int i, k; - int reallen = len / 2; - char *cstring = (char *)malloc(reallen); -- -+ if (cstring == NULL) { -+ printf("FATAL! convert_string: malloc() failed! Out of memory?\n"); -+ abort(); -+ } - for(i = 0, k = 0; i < len; i += 2, k++) - { - cstring[k] = ((char *)string)[i]; -@@ -2848,6 +2853,14 @@ +@@ -4127,6 +4131,14 @@ closeHive(hdesc); return(NULL); } diff -Nru chntpw-0.99.6/debian/patches/series chntpw-0.99.6.110511/debian/patches/series --- chntpw-0.99.6/debian/patches/series 2014-01-30 05:54:15.000000000 +0000 +++ chntpw-0.99.6.110511/debian/patches/series 2014-01-30 05:54:15.000000000 +0000 @@ -1,8 +1,8 @@ 01_port_to_gcrypt.patch #02_upstream_documents -03_keyname-overflow +#03_keyname-overflow 04_get_abs_path -05_control_empty_values +#05_control_empty_values 06_correct_test_open_syscall 07_detect_failure_to_write_key 08_no_deref_null diff -Nru chntpw-0.99.6/edlib.c chntpw-0.99.6.110511/edlib.c --- chntpw-0.99.6/edlib.c 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/edlib.c 2011-05-11 19:33:56.000000000 +0000 @@ -4,12 +4,26 @@ * Point of this is so that interactive registry editor * can be accessed from several other programs * + * 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. + * 2010-apr: Lots of bugfix and other patches from + * Frediano Ziglio + * His short patch comments: + * remove leak + * fix default value, bin and quote + * support wide char in key + * support wide character into value names + * fix export for string with embedded end lines + * remove some warnings + * compute checksum writing + * * 2008-mar: First version. Moved from chntpw.c * See HISTORY.txt for more detailed info on history. * ***** * - * Copyright (c) 1997-2007 Petter Nordahl-Hagen. + * Copyright (c) 1997-2011 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 @@ -34,9 +48,10 @@ #include "ntreg.h" -const char edlib_version[] = "edlib version 0.1 080526, (c) Petter N Hagen"; +const char edlib_version[] = "edlib version 0.1 110511, (c) Petter N Hagen"; -#undef ALLOC_DEBUG /* Reg allocation debug hooks */ +#define ALLOC_DEBUG 0 /* Reg allocation debug hooks */ +#define ADDBIN_DEBUG 0 /* Reg expansion debug hooks */ extern char *val_types[REG_MAX+1]; @@ -65,6 +80,8 @@ #define MCMD_CATHEX 18 #define MCMD_RDEL 19 #define MCMD_CK 20 +#define MCMD_CAT_DPI 21 +#define MCMD_ADDBIN 22 struct cmds maincmds[] = { { "cd" , MCMD_CD } , @@ -81,6 +98,9 @@ { "alloc", MCMD_ALLOC } , { "free", MCMD_FREE } , #endif +#if ADDBIN_DEBUG + { "addbin", MCMD_ADDBIN }, +#endif { "nv", MCMD_ADDV } , { "dv", MCMD_DELV } , { "delallv", MCMD_DELVALL } , @@ -91,9 +111,73 @@ { "ek", MCMD_EXPORTKEY }, { "ck", MCMD_CK } , { "?", MCMD_HELP } , + { "dpi", MCMD_CAT_DPI } , { "", 0 } }; +/* display decoded DigitalProductId + * nkofs = node + * path = "DigitalProductId" or some other + */ +void cat_dpi(struct hive *hdesc, int nkofs, char *path) +{ + void *data; + int len,i,type; + + type = get_val_type(hdesc, nkofs, path, 0); + if (type == -1) { + printf("cat_dpi: No such value <%s>\n",path); + return; + } + + len = get_val_len(hdesc, nkofs, path, 0); + if (len < 67) { + printf("cat_dpi: Value <%s> is too short for decoding\n",path); + return; + } + + data = (void *)get_val_data(hdesc, nkofs, path, 0, 0); + if (!data) { + printf("cat_dpi: Value <%s> references NULL-pointer (bad boy!)\n",path); + abort(); + return; + } + + if (type != REG_BINARY) { + printf ("Only binary values\n"); + return; + } + + printf("Value <%s> of type %s, data length %d [0x%x]\n", path, + (type < REG_MAX ? val_types[type] : "(unknown)"), len, len); + + + char digits[] = {'B','C','D','F','G','H','J','K','M','P','Q','R','T','V','W','X','Y','2','3','4','6','7','8','9'}; + +#define RESULT_LEN 26 + char result[RESULT_LEN]; + memset (result, 0, RESULT_LEN); + +#define START_OFFSET 52 +#define BUF_LEN 15 + unsigned char buf[BUF_LEN]; + memcpy (buf, data + START_OFFSET, BUF_LEN); + + for (i = RESULT_LEN - 2; i >= 0; i--) { + unsigned int x = 0; + + int j; + for (j = BUF_LEN - 1; j >= 0; j--) { + x = (x << 8) + buf[j]; + buf[j] = x / 24; + x = x % 24; + } + result[i] = digits[x]; + } + + printf ("\nDecoded product ID: [%s]\n", result); +} + /* display (cat) the value, * vofs = offset to 'nk' node, paths relative to this (or 0 for root) * path = path string to value @@ -103,26 +187,39 @@ { void *data; int len,i,type; - char string[SZ_MAX+1]; + // char string[SZ_MAX+1]; + char *string = NULL; + struct keyval *kv = NULL; - type = get_val_type(hdesc, nkofs, path, 0); + type = get_val_type(hdesc, nkofs, path, TPF_VK); if (type == -1) { printf("cat_vk: No such value <%s>\n",path); return; } - len = get_val_len(hdesc, nkofs, path, 0); + len = get_val_len(hdesc, nkofs, path, TPF_VK); if (!len) { printf("cat_vk: Value <%s> has zero length\n",path); return; } - data = (void *)get_val_data(hdesc, nkofs, path, 0, 0); +#if 0 + data = (void *)get_val_data(hdesc, nkofs, path, 0, TPF_VK); if (!data) { printf("cat_vk: Value <%s> references NULL-pointer (bad boy!)\n",path); abort(); return; } +#endif + + kv = get_val2buf(hdesc, NULL, nkofs, path, 0, TPF_VK); + + if (!kv) { + printf("cat_vk: Value <%s> could not fetch data\n",path); + abort(); + } + 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); @@ -132,12 +229,14 @@ case REG_SZ: case REG_EXPAND_SZ: case REG_MULTI_SZ: - cheap_uni2ascii(data,string,len); + string = string_regw2prog(data, len); + // cheap_uni2ascii(data,string,len); for (i = 0; i < (len>>1)-1; i++) { if (string[i] == 0) string[i] = '\n'; if (type == REG_SZ) break; } puts(string); + FREE(string); break; case REG_DWORD: printf("0x%08x",*(unsigned short *)data); @@ -148,6 +247,8 @@ hexdump((char *)data, 0, len, 1); } putchar('\n'); + FREE(kv); + } /* Edit value: Invoke whatever is needed to edit it @@ -288,7 +389,7 @@ newsize = atoi(inbuf); ALLOC(newkv,1,newsize+sizeof(int)+4); bzero(newkv,newsize+sizeof(int)+4); - memcpy(newkv, kv, (len < newsize) ? (len) : (newsize) +sizeof(int)); + memcpy(newkv, kv, ((len < newsize) ? (len) : (newsize)) + sizeof(int)); FREE(kv); kv = newkv; kv->len = newsize; @@ -398,6 +499,7 @@ printf("cd - change current key\n"); printf("ls | dir [] - show subkeys & values,\n"); printf("cat | type - show key value\n"); + printf("dpi - show decoded DigitalProductId value\n"); printf("hex - hexdump of value data\n"); printf("ck [] - Show keys class data, if it has any\n"); printf("nk - add key\n"); @@ -448,7 +550,7 @@ } add_value(hdesc, cdofs+4, bp, nh); break; -#ifdef ALLOC_DEBUG +#if ALLOC_DEBUG case MCMD_FREE : bp++; skipspace(&bp); @@ -462,6 +564,14 @@ alloc_block(hdesc, cdofs+4, nh); break; #endif +#if ADDBIN_DEBUG + case MCMD_ADDBIN : + bp++; + skipspace(&bp); + nh = gethex(&bp); + add_bin(hdesc, nh); + break; +#endif case MCMD_LS : bp++; skipspace(&bp); @@ -522,6 +632,11 @@ skipspace(&bp); cat_vk(hdesc,cdofs+4,bp,0); break; + case MCMD_CAT_DPI: + bp++; + skipspace(&bp); + cat_dpi (hdesc, cdofs+4, bp); + break; case MCMD_CATHEX: bp++; skipspace(&bp); @@ -559,7 +674,7 @@ if (*bp) { vkofs = gethex(&bp); } - parse_block(hdesc,vkofs,1); + parse_block(hdesc,vkofs,2); break; case MCMD_DEBUG: if (debugit(hdesc->buffer,hdesc->size)) hdesc->state |= HMODE_DIRTY; @@ -568,7 +683,7 @@ return; break; default: - printf("Unknown command: %s\n",bp); + printf("Unknown command: %s, type ? for help\n",bp); break; } } diff -Nru chntpw-0.99.6/hash.c chntpw-0.99.6.110511/hash.c --- chntpw-0.99.6/hash.c 1970-01-01 00:00:00.000000000 +0000 +++ chntpw-0.99.6.110511/hash.c 2011-05-11 19:33:56.000000000 +0000 @@ -0,0 +1,66 @@ +/* Mickeysoft hashroutine in XP 'lh' key index lists */ + +#include +#include +#include + + +static const unsigned char charset2upper[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */ + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */ + 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */ + + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */ + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */ +}; + + + + +int main(void) +{ + + char str[500]; + long hash = 0; + int i; + FILE *f; + + f = fdopen(0,"r"); + + fgets(str, 499, f); + + str[strlen(str)-1] = 0; + + for (i = 0; i < strlen(str); i++) { + hash *= 37; + hash += charset2upper[(unsigned char)str[i]]; + } + printf("hash = %08x\n",hash); +} diff -Nru chntpw-0.99.6/ntreg.c chntpw-0.99.6.110511/ntreg.c --- chntpw-0.99.6/ntreg.c 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/ntreg.c 2011-05-11 19:33:56.000000000 +0000 @@ -1,6 +1,25 @@ /* - * ntreg.c - NT Registry Hive access library - * + * ntreg.c - Windows (NT and up) Registry Hive access library + * + * 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. + * 2011-may: Expansion now seems to be working, have lot of test accepted by + * vista and win7. But no warranties.. + * 2011-may: Found a couple of nasty bugs inn add_key(), making Vista and newer + * reject (remove) keys on hive load. + * May have been there for a long time according to reports. + * 2011-apr: Fixed some problems with the allocator when at the end of a hbin. + * 2011-apr: .reg file import. Ugly one, but it seems to work. Found + * quite a lot of bugs in other places while testing it. + * String handling when international characters or wide (UTF-16) + * is a pain, and very ugly. May not work in some cases. + * Will keep wide (16 bit) characters in strings when importing from + * .reg file that has it, like what regedit.exe generates for example. + * 2011-apr: Added routines for hive expansion. Will add new hbin at end of file + * when needed. If using library, please read ugly warnings in "alloc_block()". + * 2010-jun: Patches from Frediano Ziglio adding support for wide characters + * and some bugfixes. Thank you! * 2008-mar: Type QWORD (XP/Vista and newer) now recognized * 2008-mar: Most functions accepting a path now also have a parameter specifying if * the search should be exact or on first match basis @@ -24,7 +43,7 @@ ***** * * NTREG - Window registry file reader / writer library - * Copyright (c) 1997-2007 Petter Nordahl-Hagen. + * Copyright (c) 1997-2010 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 @@ -58,7 +77,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.94 080526, (c) Petter N Hagen"; +const char ntreg_version[] = "ntreg lib routines, v0.95 110511, (c) Petter N Hagen"; const char *val_types[REG_MAX+1] = { "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", /* 0 - 4 */ @@ -67,7 +86,78 @@ "REG_QWORD", /* 11 */ }; +static char * string_prog2regw(void *string, int len, int* out_len); + /* Utility routines */ + +/* toupper() table for registry hashing functions, so we don't have to + * dependent upon external locale lib files + */ + +static const unsigned char reg_touppertable[] = { + + /* ISO 8859-1 is probably not the one.. */ + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */ + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */ + 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */ + + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */ + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */ + +}; + + +/* Use table above in strcasecmp else add_key may put names in wrong order + and windows actually verifies that on hive load!! + or at least it finds out in some cases.. +*/ + + +int strn_casecmp(const char *s1, const char *s2, size_t n) +{ + char r; + + while ( *s1 && *s2 && n ) { + r = (unsigned char)reg_touppertable[(unsigned char)*s1] - (unsigned char)reg_touppertable[(unsigned char)*s2]; + if (r) return(r); + n--; + s1++; + s2++; + } + if ( (!*s1 && !*s2) || !n) return(0); + if ( !*s1 ) return(-1); + return(1); +} + + char *str_dup( const char *str ) { char *str_new; @@ -80,6 +170,24 @@ return str_new; } +/* Copy non-terminated string to buffer we allocate and null terminate it + * Uses length only, does not check for nulls + */ + +char *mem_str( const char *str, int len ) +{ + char *str_new; + + if (!str) + return 0 ; + + CREATE( str_new, char, len + 1 ); + memcpy( str_new, str, len); + *(str_new+len) = 0; + return str_new; +} + + int fmyinput(char *prmpt, char *ibuf, int maxlen) { @@ -273,32 +381,6 @@ hexdump(buf,from,to,0); cofs = to; break; -#if 0 - case 'k' : - bp++; - if (*bp) { - from = gethex(&bp); - } else { - from = cofs; - } - if (to > sz) to = sz; - parse_block(from,1); - cofs = to; - break; -#endif -#if 0 - case 'l' : - bp++; - if (*bp) { - from = gethex(&bp); - } else { - from = cofs; - } - if (to > sz) to = sz; - nk_ls(from+4,0); - cofs = to; - break; -#endif case 'q': return(0); break; @@ -338,19 +420,7 @@ memcpy(buf+from,whatbuf,wlen); dirty = 1; break; -#if 0 - case 'p': - j = 0; - if (*(++bp) != 0) { - from = gethex(&bp); - } - if (*(++bp) != 0) { - j = gethex(&bp); - } - printf("from: %x, rid: %x\n",from,j); - seek_n_destroy(from,j,500,0); - break; -#endif + case '?': printf("d [] [] - dump buffer within range\n"); printf("a [] [] - same as d, but without ascii-part (for cut'n'paste)\n"); @@ -397,14 +467,20 @@ printf("%04x type = 0x%02x %s\n", D_OFFS(type) ,key->type, (key->type == KEY_ROOT ? "ROOT_KEY" : "") ); printf("%04x timestamp skipped\n", D_OFFS(timestamp) ); - printf("%04x parent key offset = 0x%0x\n", D_OFFS(ofs_parent) ,key->ofs_parent); + printf("%04x parent key offset = 0x%0x\n", D_OFFS(ofs_parent) ,key->ofs_parent + 0x1000); printf("%04x number of subkeys = %d\n", D_OFFS(no_subkeys),key->no_subkeys); - printf("%04x lf-record offset = 0x%0x\n",D_OFFS(ofs_lf),key->ofs_lf); + printf("%04x lf-record offset = 0x%0x\n",D_OFFS(ofs_lf),key->ofs_lf + 0x1000); printf("%04x number of values = %d\n", D_OFFS(no_values),key->no_values); - printf("%04x val-list offset = 0x%0x\n",D_OFFS(ofs_vallist),key->ofs_vallist); - printf("%04x sk-record offset = 0x%0x\n",D_OFFS(ofs_sk),key->ofs_sk); - printf("%04x classname offset = 0x%0x\n",D_OFFS(ofs_classnam),key->ofs_classnam); - printf("%04x *unused?* = 0x%0x\n",D_OFFS(dummy4),key->dummy4); + printf("%04x val-list offset = 0x%0x\n",D_OFFS(ofs_vallist),key->ofs_vallist + 0x1000); + printf("%04x sk-record offset = 0x%0x\n",D_OFFS(ofs_sk),key->ofs_sk + 0x1000); + printf("%04x classname offset = 0x%0x\n",D_OFFS(ofs_classnam),key->ofs_classnam + 0x1000); + + printf("%04x dummy3 = 0x%0x (%d)\n",D_OFFS(dummy3),key->dummy3,key->dummy3); + printf("%04x dummy4 = 0x%0x (%d)\n",D_OFFS(dummy4),key->dummy4,key->dummy4); + printf("%04x dummy5 = 0x%0x (%d)\n",D_OFFS(dummy5),key->dummy5,key->dummy5); + printf("%04x dummy6 = 0x%0x (%d)\n",D_OFFS(dummy6),key->dummy6,key->dummy6); + printf("%04x dummy7 = 0x%0x (%d)\n",D_OFFS(dummy7),key->dummy7,key->dummy7); + printf("%04x name length = %d\n", D_OFFS(len_name),key->len_name); printf("%04x classname length = %d\n", D_OFFS(len_classnam),key->len_classnam); @@ -430,7 +506,7 @@ key->len_name, key->len_name ); printf("%04x length of data = %d (0x%0x)\n", D_OFFS(len_data), key->len_data, key->len_data ); - printf("%04x data offset = 0x%0x\n",D_OFFS(ofs_data),key->ofs_data); + printf("%04x data offset = 0x%0x\n",D_OFFS(ofs_data),key->ofs_data + 0x1000); printf("%04x value type = 0x%0x %s\n", D_OFFS(val_type), key->val_type, (key->val_type <= REG_MAX ? val_types[key->val_type] : "(unknown)") ) ; @@ -456,8 +532,8 @@ key = (struct sk_key *)(hdesc->buffer + vofs); printf("%04x *unused?* = %d\n" , D_OFFS(dummy1), key->dummy1 ); - printf("%04x Offset to prev sk = 0x%0x\n", D_OFFS(ofs_prevsk), key->ofs_prevsk); - printf("%04x Offset to next sk = 0x%0x\n", D_OFFS(ofs_nextsk), key->ofs_nextsk); + printf("%04x Offset to prev sk = 0x%0x\n", D_OFFS(ofs_prevsk), key->ofs_prevsk + 0x1000); + printf("%04x Offset to next sk = 0x%0x\n", D_OFFS(ofs_nextsk), key->ofs_nextsk + 0x1000); printf("%04x Usage counter = %d (0x%0x)\n", D_OFFS(no_usage), key->no_usage,key->no_usage); printf("%04x Security data len = %d (0x%0x)\n", D_OFFS(len_sk), @@ -484,7 +560,7 @@ for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x - <%c%c%c%c>\n", D_OFFS(hash[i].ofs_nk), i, - key->hash[i].ofs_nk, + key->hash[i].ofs_nk + 0x1000, key->hash[i].name[0], key->hash[i].name[1], key->hash[i].name[2], @@ -512,7 +588,7 @@ for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x - \n", D_OFFS(lh_hash[i].ofs_nk), i, - key->lh_hash[i].ofs_nk, + key->lh_hash[i].ofs_nk + 0x1000, key->lh_hash[i].hash ); } @@ -539,7 +615,7 @@ for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x\n", D_OFFS(hash[i].ofs_nk), i, - key->hash[i].ofs_nk); + key->hash[i].ofs_nk + 0x1000); } printf("== End of key info.\n"); @@ -564,13 +640,35 @@ for(i = 0; i < key->no_lis; i++) { printf("%04x %3d Offset: 0x%0x\n", D_OFFS(hash[i].ofs_li), i, - key->hash[i].ofs_li); + key->hash[i].ofs_li + 0x1000); } printf("== End of key info.\n"); } +/* Parse the db block (used when value data >4k or something) + * vofs = offset into struct (after size linkage) + */ +void parse_db(struct hive *hdesc, int vofs, int blen) +{ + struct db_key *key; + + printf("== db at offset %0x\n",vofs); + + key = (struct db_key *)(hdesc->buffer + vofs); + printf("%04x number of parts = %d\n", D_OFFS(no_part), key->no_part ); + + printf("%04x Data list at offset: 0x%0x\n", + D_OFFS(ofs_data), + key->ofs_data + 0x1000); + + printf("== End of key info.\n"); + +} + + + /* Parse the datablock * vofs = offset into struct (after size linkage) */ @@ -580,14 +678,18 @@ unsigned short id; int seglen; - seglen = get_int(hdesc->buffer+vofs); + seglen = get_int(hdesc->buffer+vofs); + // if (vofs > 0xaef000) verbose = 1; + +#if 0 if (verbose || seglen == 0) { printf("** Block at offset %0x\n",vofs); printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen); } +#endif if (seglen == 0) { - printf("Whoops! FATAL! Zero data block size! (not registry or corrupt file?)\n"); + printf("parse_block: FATAL! Zero data block size! (not registry or corrupt file?)\n"); debugit(hdesc->buffer,hdesc->size); return(0); } @@ -597,7 +699,7 @@ hdesc->usetot += seglen; hdesc->useblk++; if (verbose) { - printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen); + printf("USED BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen); /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } } else { @@ -609,18 +711,16 @@ #endif if (verbose) { - printf("FREE BLOCK!\n"); + printf("FREE BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen); /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } } - /* printf("Seglen: 0x%02x\n",seglen & 0xff ); */ - vofs += 4; id = (*(hdesc->buffer + vofs)<<8) + *(hdesc->buffer+vofs+1); - if (verbose) { + if (verbose > 1) { switch (id) { case 0x6e6b: /* nk */ parse_nk(hdesc, vofs, seglen); @@ -643,6 +743,9 @@ case 0x7269: /* ri */ parse_ri(hdesc, vofs, seglen); break; + case 0x6462: /* db */ + parse_db(hdesc, vofs, seglen); + break; default: printf("value data, or not handeled yet!\n"); break; @@ -703,12 +806,20 @@ seglen = get_int(hdesc->buffer+vofs); #if FB_DEBUG + if (vofs > 0x400000) { printf("** Block at offset %0x\n",vofs); printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen); + } #endif if (seglen == 0) { printf("find_free_blk: FATAL! Zero data block size! (not registry or corrupt file?)\n"); + printf(" : Block at offset %0x\n",vofs); + if ( (vofs - pofs) == (p->ofs_next - 4) ) { + printf("find_free_blk: at exact end of hbin, do not care..\n"); + return(0); + } + abort(); debugit(hdesc->buffer,hdesc->size); return(0); } @@ -716,17 +827,17 @@ if (seglen < 0) { seglen = -seglen; #if FB_DEBUG - printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen); + if (vofs >0x400000) printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen); #endif /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ } else { #if FB_DEBUG - printf("FREE BLOCK!\n"); + if (vofs >0x400000) printf("FREE BLOCK!\n"); #endif /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ if (seglen >= size) { #if FB_DEBUG - printf("find_free_blk: found size %d block at 0x%x\n",seglen,vofs); + if (vofs >0x400000) printf("find_free_blk: found size %d block at 0x%x\n",seglen,vofs); #endif #if 0 if (vofs == 0x19fb8) { @@ -763,7 +874,7 @@ /* Again, assume start at 0x1000 */ r = 0x1000; - while (r < hdesc->size) { + while (r < hdesc->endofs) { h = (struct hbin_page *)(hdesc->buffer + r); if (h->id != 0x6E696268) return(0); if (h->ofs_next == 0) { @@ -777,11 +888,97 @@ return(0); } +/* Add new hbin to end of file. If file contains data at end + * that is not in a hbin, include that too + * hdesc - hive as usual + * size - minimum size (will be rounded up to next 0x1000 alignment) + * returns offset to first block in new hbin + */ + +#define ADDBIN_DEBUG +int add_bin(struct hive *hdesc, int size) +{ + int r,newsize,newbinofs; + struct hbin_page *newbin; + struct regf_header *hdr; + + if (hdesc->state & HMODE_NOEXPAND) { + fprintf(stderr,"ERROR: Registry hive <%s> need to be expanded,\n" + "but that is not allowed according to selected options. Operations will fail.\n", hdesc->filename); + return(0); + } + + r = ((size + 0x20 + 4) & ~0xfff) + HBIN_PAGESIZE; /* Add header and link, round up to page boundary, usually 0x1000 */ + + newbinofs = hdesc->endofs; + + +#ifdef ADDBIN_DEBUG + printf("add_bin: request size = %d [%x], rounded to %d [%x]\n",size,size,r,r); + printf("add_bin: old buffer size = %d [%x]\n",hdesc->size,hdesc->size); + printf("add_bin: firs nonbin off = %d [%x]\n",newbinofs,newbinofs); + printf("add_bin: free at end = %d [%x]\n",hdesc->size-newbinofs,hdesc->size-newbinofs); +#endif + + if ( (newbinofs + r) >= hdesc->size) { /* We must allocate more buffer */ + newsize = ( (newbinofs + r) & ~(REGF_FILEDIVISOR-1) ) + REGF_FILEDIVISOR; /* File normally multiple of 0x40000 bytes */ + +#ifdef ADDBIN_DEBUG + printf("add_bin: new buffer size = %d [%x]\n",newsize,newsize); +#endif + + hdesc->buffer = realloc(hdesc->buffer, newsize); + if (!hdesc->buffer) { + perror("add_bin : realloc() "); + abort(); + } + hdesc->size = newsize; + + } + + /* At this point, we have large enough space at end of file */ + + newbin = (struct hbin_page *)(hdesc->buffer + newbinofs); + + bzero((void *)newbin, r); /* zero out new hbin, easier to debug too */ + + newbin->id = 0x6E696268; /* 'hbin' */ + newbin->ofs_self = newbinofs - 0x1000; /* Point to ourselves minus regf. Seem to be that.. */ + newbin->ofs_next = r; /* size of this new bin */ + + /* Wonder if anything else in the hbin header matters? */ + + /* Set whole hbin to be one contious unused block */ + newbin->firstlink = (r - 0x20 - 0); /* Positive linkage = unused */ + + /* Update REGF header */ + hdr = (struct regf_header *) hdesc->buffer; + hdr->filesize = newbinofs + r - 0x1000; /* Point header to new end of data */ + +#ifdef ADDBIN_DEBUG + printf("add_bin: adjusting size field in REGF: %d [%x]\n",hdr->filesize,hdr->filesize); +#endif + + /* Update state */ + + hdesc->state |= HMODE_DIDEXPAND | HMODE_DIRTY; + hdesc->lastbin = newbinofs; /* Last bin */ + hdesc->endofs = newbinofs + r; /* New data end */ + + return(newbinofs + 0x20); + +} + + + /* Allocate a block of requested size if possible * hdesc - hive * pofs - If >0 will try this page first (ptr may be inside page) * size - number of bytes to allocate * returns: 0 - failed, else pointer to allocated block. + * WARNING: Will realloc() buffer if it has to be expanded! + * ALL POINTERS TO BUFFER IS INVALID AFTER THAT. (offsets are still correct) + * Guess I'd better switch to mmap() one day.. * This function WILL CHANGE THE HIVE (change block linkage) if it * succeeds. */ @@ -790,10 +987,11 @@ { int pofs = 0; int blk = 0; + int newbin; int trail, trailsize, oldsz; if (hdesc->state & HMODE_NOALLOC) { - printf("alloc_block: ERROR: Hive %s is in no allocation safe mode," + printf("\nERROR: alloc_block: Hive <%s> is in no allocation safe mode," "new space not allocated. Operation will fail!\n", hdesc->filename); return(0); } @@ -870,10 +1068,17 @@ #endif hdesc->state |= HMODE_DIRTY; - + +#if 0 + printf("alloc_block: returning %x\n",blk); +#endif return(blk); } else { - printf("alloc_block: failed to alloc %d bytes, and hive expansion not implemented yet!\n",size); + printf("alloc_block: failed to alloc %d bytes, trying to expand hive..\n",size); + + newbin = add_bin(hdesc,size); + if (newbin) return(alloc_block(hdesc,newbin,size)); /* Nasty... recall ourselves. */ + /* Fallthrough to fail if add_bin fails */ } return(0); } @@ -1092,14 +1297,26 @@ } else { if (newnkkey->len_name <= 0) { printf("ex_next: nk at 0x%0x has no name!\n",newnkofs); + } else if (newnkkey->type & 0x20) { +#if 0 + printf("dummy1 %x\n", *((int*)newnkkey->dummy1)); + printf("dummy2 %x\n", *((int*)newnkkey->dummy2)); + printf("type %x\n", newnkkey->type); + printf("timestamp+8 %x\n", *((int*)(newnkkey->timestamp+8))); + printf("dummy3+0 %x\n", *((int*)(newnkkey->dummy3+0))); + printf("dummy3+4 %x\n", *((int*)(newnkkey->dummy3+4))); + printf("dummy3+8 %x\n", *((int*)(newnkkey->dummy3+8))); + printf("dummy3+12 %x\n", *((int*)(newnkkey->dummy3+12))); + printf("dummy4 %x\n", *((int*)&newnkkey->dummy4)); + printf("len %d\n", newnkkey->len_name); + printf("len class %d\n", newnkkey->len_classnam); + fflush(stdout); +#endif + + sptr->name = mem_str(newnkkey->keyname,newnkkey->len_name); + // sptr->name = string_rega2prog(newnkkey->keyname, newnkkey->len_name); } else { - sptr->name = (char *)malloc(newnkkey->len_name+1); - if (!sptr->name) { - printf("FATAL! ex_next: malloc() failed! Out of memory?\n"); - abort(); - } - strncpy(sptr->name,newnkkey->keyname,newnkkey->len_name); - sptr->name[newnkkey->len_name] = 0; + sptr->name = string_regw2prog(newnkkey->keyname, newnkkey->len_name); } } /* if */ (*count)++; @@ -1152,21 +1369,28 @@ sptr->size = (vkkey->len_data & 0x7fffffff); if (vkkey->len_name >0) { - CREATE(sptr->name,char,vkkey->len_name+1); - memcpy(sptr->name,vkkey->keyname,vkkey->len_name); - sptr->name[vkkey->len_name] = 0; + if (vkkey->flag & 1) { + + sptr->name = mem_str(vkkey->keyname, vkkey->len_name); + // sptr->name = string_rega2prog(vkkey->keyname, vkkey->len_name); + } else { + sptr->name = string_regw2prog(vkkey->keyname, vkkey->len_name); + } } else { - sptr->name = str_dup("@"); + sptr->name = str_dup(""); } sptr->type = vkkey->val_type; + if (sptr->size) { if (vkkey->val_type == REG_DWORD) { if (vkkey->len_data & 0x80000000) { sptr->val = (int)(vkkey->ofs_data); } } - } else if (vkkey->len_data == 0x80000000) { + } +#if 0 + else if (vkkey->len_data == 0x80000000) { /* Data SIZE is 0, high bit set: special inline case, data is DWORD and in TYPE field!! */ /* Used a lot in SAM, and maybe in SECURITY I think */ sptr->val = (int)(vkkey->val_type); @@ -1176,6 +1400,7 @@ sptr->val = 0; sptr->size = 0; } +#endif (*count)++; return( *count <= key->no_values ); @@ -1194,6 +1419,8 @@ /* int newnkofs; */ struct nk_key *key; char tmp[ABSPATHLEN+1]; + char *keyname; + int len_name; maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN); @@ -1210,13 +1437,21 @@ strncpy(tmp,path,ABSPATHLEN-1); - if ( (strlen(path) + key->len_name) >= maxlen-6) { + if (key->type & 0x20) + keyname = mem_str(key->keyname, key->len_name); + // keyname = string_rega2prog(key->keyname, key->len_name); + else + keyname = string_regw2prog(key->keyname, key->len_name); + len_name = strlen(keyname); + if ( (strlen(path) + len_name) >= maxlen-6) { + free(keyname); snprintf(path,maxlen,"(...)%s",tmp); return(strlen(path)); /* Stop trace when string exhausted */ } *path = '\\'; - memcpy(path+1,key->keyname,key->len_name); - strncpy(path+key->len_name+1,tmp,maxlen); + memcpy(path+1,keyname,len_name); + free(keyname); + strncpy(path+len_name+1,tmp,maxlen-6-len_name); return(get_abs_path(hdesc, key->ofs_parent+0x1004, path, maxlen)); /* go back one more */ } @@ -1233,23 +1468,33 @@ struct vk_key *vkkey; int i,vkofs,len; int32_t *vlistkey; + int approx = -1; len = strlen(name); vlistkey = (int32_t *)(hdesc->buffer + vlistofs); + // printf("vlist_find: <%s> len = %d\n",name,len); + for (i = 0; i < numval; i++) { vkofs = vlistkey[i] + 0x1004; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); - if (vkkey->len_name == 0 && *name == '@') { /* @ is alias for nameless value */ + if (vkkey->len_name == 0 && *name == '@' && len == 1) { /* @ is alias for nameless value */ return(i); } - if ( !(type & TPF_EXACT) || vkkey->len_name == len ) { - if (!strncmp(name, vkkey->keyname, len)) { /* name match? */ - return(i); + + // printf("vlist_find: matching against: <%s> len = %d\n",vkkey->keyname,vkkey->len_name); + + if ( (type & TPF_EXACT) && vkkey->len_name != len ) continue; /* Skip if exact match and not exact size */ + + if ( vkkey->len_name >= len ) { /* Only check for names that are longer or equal than we seek */ + if ( !strncmp(name, vkkey->keyname, len) ) { /* Name match */ + if (vkkey->len_name == len) return(i); /* Exact match always best, returns */ + if (approx == -1) approx = i; /* Else remember first partial match */ } } + } - return(-1); + return(approx); } @@ -1277,47 +1522,69 @@ if (!hdesc) return(0); buf = hdesc->buffer; + // printf("trav_path: called with vofs = %x, path = <%s>, type = %x\n",vofs, path, type); + + if (!vofs) vofs = hdesc->rootofs+4; /* No current key given , so start at root */ - if (*path == '\\' && *(path+1) != '\\') { /* Start from root if path starts with \ */ + if ( !(type & TPF_ABS) && *path == '\\' && *(path+1) != '\\') { /* Start from root if path starts with \ */ path++; vofs = hdesc->rootofs+4; } key = (struct nk_key *)(buf + vofs); - /* printf("check of nk at offset: 0x%0x\n",vofs); */ + // printf("check of nk at offset: 0x%0x\n",vofs); if (key->id != 0x6b6e) { printf("trav_path: Error: Not a 'nk' node!\n"); return(0); } - /* Find \ delimiter or end of string, copying to name part buffer as we go, - rewriting double \\s */ - partptr = part; - for(plen = 0; path[plen] && (path[plen] != '\\' || path[plen+1] == '\\'); plen++) { - if (path[plen] == '\\' && path[plen+1] == '\\') plen++; /* Skip one if double */ - *partptr++ = path[plen]; - } - *partptr = '\0'; - - /* printf("Name component: <%s>\n",part); */ - - adjust = (path[plen] == '\\' ) ? 1 : 0; - /* printf("Checking for <%s> with len %d\n",path,plen); */ - if (!plen) return(vofs-4); /* Path has no lenght - we're there! */ - if ( (plen == 1) && (*path == '.') && !(type & TPF_EXACT)) { /* Handle '.' current dir */ - return(trav_path(hdesc,vofs,path+plen+adjust,type)); - } - if ( !(type & TPF_EXACT) && (plen == 2) && !strncmp("..",path,2) ) { /* Get parent key */ - newnkofs = key->ofs_parent + 0x1004; - /* Return parent (or only root if at the root) */ - return(trav_path(hdesc, (key->type == KEY_ROOT ? vofs : newnkofs), path+plen+adjust, type)); + if ( !(type & TPF_ABS)) { /* Only traverse path if not absolute literal value name passed */ + + /* TODO: Need to rethink this.. */ + + /* Find \ delimiter or end of string, copying to name part buffer as we go, + rewriting double \\s */ + partptr = part; + for(plen = 0; path[plen] && (path[plen] != '\\' || path[plen+1] == '\\'); plen++) { + if (path[plen] == '\\' && path[plen+1] == '\\') plen++; /* Skip one if double */ + *partptr++ = path[plen]; + } + *partptr = '\0'; + +#if 0 + printf("Name part: <%s>\n",part); + printf("Name path: <%s>\n",path); +#endif + + adjust = (path[plen] == '\\' ) ? 1 : 0; + // printf("Checking for <%s> with len %d\n",path,plen); + + if (!plen) return(vofs-4); /* Path has no lenght - we're there! */ + + if ( (plen == 1) && (*(path+1) && *path == '.') && !(type & TPF_EXACT)) { /* Handle '.' current dir */ + // printf("** handle current\n"); + return(trav_path(hdesc,vofs,path+plen+adjust,type)); + } + if ( !(type & TPF_EXACT) && (plen == 2) && !strncmp("..",path,2) ) { /* Get parent key */ + newnkofs = key->ofs_parent + 0x1004; + /* Return parent (or only root if at the root) */ + return(trav_path(hdesc, (key->type == KEY_ROOT ? vofs : newnkofs), path+plen+adjust, type)); + } + } /* at last name of path, and we want vk, and the nk has values */ - if (!path[plen] && (type & TPF_VK) && key->no_values) { - /* printf("VK namematch for <%s>\n",part); */ + if ((type & TPF_VK_ABS) || (!path[plen] && (type & TPF_VK) && key->no_values) ) { + // if ( (!path[plen] && (type & TPF_VK) && key->no_values) ) { + if (type & TPF_ABS) { + strcpy(part, path); + plen = de_escape(part,0); + partptr = part + plen; + } + + // printf("VK namematch for <%s>, type = %d\n",part,type); vlistofs = key->ofs_vallist + 0x1004; vlistkey = (int32_t *)(buf + vlistofs); i = vlist_find(hdesc, vlistofs, key->no_values, part, type); @@ -1327,6 +1594,11 @@ } if (key->no_subkeys > 0) { /* If it has subkeys, loop through the hash */ + char *partw = NULL; + int partw_len, part_len; + + // printf("trav_path: subkey loop: path = %s, part = %s\n",path,part); + lfofs = key->ofs_lf + 0x1004; /* lf (hash) record */ lfkey = (struct lf_key *)(buf + lfofs); @@ -1348,9 +1620,13 @@ } else { likey = NULL; } + rikey = NULL; ricnt = 0; r = 0; subs = key->no_subkeys; } + partw = string_prog2regw(part, partptr-part, &partw_len); + // string_prog2rega(part, partptr-part); + part_len = strlen(part); do { for(i = 0; i < subs; i++) { if (likey) newnkofs = likey->hash[i].ofs_nk + 0x1004; @@ -1361,9 +1637,20 @@ } else { if (newnkkey->len_name <= 0) { printf("[No name]\n"); - } else { - if (!strncmp(part,newnkkey->keyname,plen)) { - /* printf("Key at 0x%0x matches! recursing!\n",newnkofs); */ + } else if ( + ( ( part_len <= newnkkey->len_name ) && !(type & TPF_EXACT) ) || + ( ( part_len == newnkkey->len_name ) && (type & TPF_EXACT) ) + ) { + /* Can't match if name is shorter than we look for */ + int cmp; + // printf("trav_path: part = <%s>, part_len = %d\n",part,part_len); + if (newnkkey->type & 0x20) + cmp = strncmp(part,newnkkey->keyname,part_len); + else + cmp = memcmp(partw, newnkkey->keyname, partw_len); + if (!cmp) { + // printf("Key at 0x%0x matches! recursing! new path = %s\n",newnkofs,path+plen+adjust); + free(partw); return(trav_path(hdesc, newnkofs, path+plen+adjust, type)); } } @@ -1380,8 +1667,10 @@ } } } while (r < ricnt && ricnt); + free(partw); } /* if subkeys */ + /* Not found */ return(0); } @@ -1434,7 +1723,7 @@ if (key->no_values) { 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, vex.size, + if (hdesc->state & HMODE_VERBOSE) printf("[%6x] %6d %-16s <%s>", vex.vkoffs - 4, vex.size, (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name); else printf("%6d %-16s <%s>", vex.size, @@ -1459,9 +1748,7 @@ } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); -#if 0 - if (vkkey->len_data & 0x80000000) return(REG_DWORD); /* Special case of INLINE storage */ -#endif + return(vkkey->val_type); } @@ -1482,7 +1769,7 @@ len = vkkey->len_data & 0x7fffffff; - if ( vkkey->len_data == 0x80000000 ) { /* Special inline case, return size of 4 (dword) */ + if ( vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case, return size of 4 (dword) */ len = 4; } @@ -1498,16 +1785,23 @@ struct vk_key *vkkey; int vkofs; + // printf("get_val_data: path = %s\n",path); + vkofs = trav_path(hdesc,vofs,path,exact | TPF_VK); if (!vkofs) { + printf("get_val_data: %s not found\n",path); + abort(); return NULL; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); - if (vkkey->len_data == 0) return NULL; - if (vkkey->len_data == 0x80000000) { /* Special inline case (len = 0x80000000) */ + if (vkkey->len_data == 0) { + return NULL; + } + + if (vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case (len = 0x80000000) */ return(&vkkey->val_type); /* Data (4 bytes?) in type field */ } @@ -1537,26 +1831,64 @@ struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type, int exact ) { - int l; + int l,i,parts,list,blockofs,blocksize,point,copylen,restlen; struct keyval *kr; void *keydataptr; + struct db_key *db; + void *addr; l = get_val_len(hdesc, vofs, path, exact); if (l == -1) return(NULL); /* error */ if (kv && (kv->len < l)) return(NULL); /* Check for overflow of supplied buffer */ keydataptr = get_val_data(hdesc, vofs, path, type, exact); - /* if (!keydataptr) return(NULL); error */ + // if (!keydataptr) return(NULL); /* Allocate space for data + header, or use supplied buffer */ if (kv) { kr = kv; } else { - ALLOC(kr,1,l+sizeof(int)+4); + ALLOC(kr,1,l*sizeof(int)+4); } kr->len = l; - memcpy(&(kr->data), keydataptr, l); + + // printf("get_val2buf: keydataprtr = %x, l = %x\n",keydataptr,l); + + + if (l > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */ + db = (struct db_key *)keydataptr; + if (db->id != 0x6264) abort(); + parts = db->no_part; + list = db->ofs_data + 0x1004; + printf("get_val2buf: Long value: parts = %d, list = %x\n",parts,list); + + point = 0; + restlen = l; + for (i = 0; i < parts; i++) { + blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000; + blocksize = -get_int(hdesc->buffer + blockofs) - 8; + + /* Copy this part, up to size of block or rest lenght in last block */ + copylen = (blocksize > restlen) ? restlen : blocksize; + + printf("get_val2buf: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize); + printf(" : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen); + + addr = (void *)&(kr->data) + point; + memcpy( addr, hdesc->buffer + blockofs + 4, copylen); + + // debugit((char *)&(kr->data), l); + + point += copylen; + restlen -= copylen; + + } + + + } else { + if (l && kr && keydataptr) memcpy(&(kr->data), keydataptr, l); + } return(kr); } @@ -1609,28 +1941,50 @@ /* Free actual data of a value, and update value descriptor * hdesc - hive - * vofs - current key - * path - path to value - * we return offset of vk + * vofs - current value offset */ -int free_val_data(struct hive *hdesc, int vofs, char *path, int exact) +int free_val_data(struct hive *hdesc, int vkofs) { struct vk_key *vkkey; - int vkofs, inl; + struct db_key *db; + int len,i,blockofs,blocksize,parts,list; + - vkofs = trav_path(hdesc,vofs,path,1); - if (!vkofs) { - return 0; - } - vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); - inl = (vkkey->len_data & 0x80000000); + len = vkkey->len_data; + + if (!(len & 0x80000000)) { /* Check for inline, if so, skip it, nothing to do */ + + if (len > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */ + + db = (struct db_key *)(hdesc->buffer + vkkey->ofs_data + 0x1004); + + if (db->id != 0x6264) abort(); + + parts = db->no_part; + list = db->ofs_data + 0x1004; + + printf("free_val_data: Long value: parts = %d, list = %x\n",parts,list); + + for (i = 0; i < parts; i++) { + blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000; + blocksize = -get_int(hdesc->buffer + blockofs); + printf("free_val_data: Freeing long datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize); + free_block(hdesc, blockofs); + } + + printf("free_val_data: Freeing indirect list at %x\n", list-4); + free_block(hdesc, list - 4); + printf("free_val_data: Freeing db structure at %x\n", vkkey->ofs_data + 0x1000); + } /* Fall through to regular which deallocs data or db block ofs_data point to */ + + if (len) free_block(hdesc, vkkey->ofs_data + 0x1000); + + } /* inline check */ + - if (!inl) { - free_block(hdesc, vkkey->ofs_data + 0x1000); - } vkkey->len_data = 0; vkkey->ofs_data = 0; @@ -1638,7 +1992,8 @@ } -/* Allocate data for value, realloc if it already contains stuff + +/* Allocate data for value. Frees old data (if any) which will be destroyed * hdesc - hive * vofs - current key * path - path to value @@ -1649,10 +2004,12 @@ int alloc_val_data(struct hive *hdesc, int vofs, char *path, int size,int exact) { struct vk_key *vkkey; - int vkofs, len; - int datablk; + struct db_key *db; + int vkofs,dbofs,listofs,blockofs,blocksize,parts; + int datablk,i; + int *ptr; - vkofs = trav_path(hdesc,vofs,path,1); + vkofs = trav_path(hdesc,vofs,path,exact); if (!vkofs) { return (0); } @@ -1660,19 +2017,56 @@ vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); + + free_val_data(hdesc, vkofs); /* Get rid of old data if any */ + /* Allocate space for new data */ - datablk = alloc_block(hdesc, vkofs, size); + if (size > 4) { + if (size > VAL_DIRECT_LIMIT) { /* We must allocate indirect stuff *sigh* */ + parts = size / VAL_DIRECT_LIMIT + 1; + printf("alloc_val_data: doing large key: size = %x (%d), parts = %d\n",size,size,parts); + + dbofs = alloc_block(hdesc, vkofs, sizeof(struct db_key)); /* Alloc db structure */ + db = (struct db_key *)(hdesc->buffer + dbofs + 4); + db->id = 0x6264; + db->no_part = parts; + listofs = alloc_block(hdesc, vkofs, 4 * parts); /* block offset list */ + db = (struct db_key *)(hdesc->buffer + dbofs + 4); + db->ofs_data = listofs - 0x1000; + printf("alloc_val_data: dbofs = %x, listofs = %x\n",dbofs,listofs); + + for (i = 0; i < parts; i++) { + blocksize = VAL_DIRECT_LIMIT; /* Windows seem to alway allocate the whole block */ + blockofs = alloc_block(hdesc, vkofs, blocksize); + printf("alloc_val_data: block # %d, blockofs = %x\n",i,blockofs); + ptr = (int *)(hdesc->buffer + listofs + 4 + (i << 2)); + *ptr = blockofs - 0x1000; + } + datablk = dbofs; + + } else { /* Regular size < 16 k direct alloc */ + datablk = alloc_block(hdesc, vkofs, size); + } + + + + } else { /* 4 bytes or less are inlined */ + datablk = vkofs + (int32_t)&(vkkey->ofs_data) - (int32_t)vkkey; + size |= 0x80000000; + } + if (!datablk) return(0); - len = vkkey->len_data & 0x7fffffff; + vkkey = (struct vk_key *)(hdesc->buffer + vkofs); /* alloc_block may move pointer, realloc() buf */ + + // printf("alloc_val_data: datablk = %x, size = %x, vkkey->len_data = %x\n",datablk, size, vkkey->len_data); + - /* Then we dealloc if something was there before */ - if (len) free_val_data(hdesc,vofs,path,exact); /* Link in new datablock */ - vkkey->ofs_data = datablk - 0x1000; + if ( !(size & 0x80000000)) vkkey->ofs_data = datablk - 0x1000; vkkey->len_data = size; - + return(datablk + 4); } @@ -1703,7 +2097,7 @@ return(NULL); } - if (trav_path(hdesc, nkofs, name, 1)) { + if (vlist_find(hdesc, nk->ofs_vallist + 0x1004, nk->no_values, name, TPF_EXACT) != -1) { printf("add_value: value %s already exists\n",name); return(NULL); } @@ -1717,6 +2111,9 @@ printf("add_value: failed to allocate new value list!\n"); return(NULL); } + + nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */ + if (oldvlist) { /* Copy old data if any */ memcpy(hdesc->buffer + newvlist + 4, hdesc->buffer + oldvlist + 0x1004, nk->no_values * 4 + 4); } @@ -1729,6 +2126,9 @@ return(NULL); } + nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */ + + /* Success, now fill in the metadata */ newvkkey = (struct vk_key *)(hdesc->buffer + newvkofs + 4); @@ -1742,13 +2142,13 @@ if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) { newvkkey->len_data = 0x80000004; /* Prime the DWORD inline stuff */ } else { - newvkkey->len_data = 0x00000000; + newvkkey->len_data = 0x80000000; /* Default inline zero size */ } newvkkey->ofs_data = 0; newvkkey->val_type = type; - newvkkey->flag = 1; /* Don't really know what this is */ + newvkkey->flag = newvkkey->len_name ? 1 : 0; /* Seems to be 1, but 0 for no name default value */ newvkkey->dummy1 = 0; - strcpy((char *)&newvkkey->keyname, name); /* And copy name */ + memcpy((char *)&newvkkey->keyname, name, newvkkey->len_name); /* And copy name */ /* Finally update the key and free the old valuelist */ nk->no_values++; @@ -1776,7 +2176,7 @@ } if ( !(vk->len_data & 0x80000000) && vk->ofs_data) { - free_block(hdesc, vk->ofs_data + 0x1000); + free_val_data(hdesc, vkofs); } free_block(hdesc, vkofs - 4); @@ -1878,6 +2278,8 @@ printf("del_value: FATAL: Was not able to alloc new index list\n"); abort(); } + nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved */ + /* 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++) { @@ -1901,7 +2303,8 @@ * return: ptr to new keystruct, or NULL */ -#define AKDEBUG 1 +#undef AKDEBUG + struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name) { @@ -1989,7 +2392,7 @@ printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname); #endif - cmp = strncasecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name); + cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name); if (!cmp) { printf("add_key: key %s already exists!\n",name); FREE(newli); @@ -2029,7 +2432,7 @@ #if 0 printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname); #endif - cmp = strncasecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name); + cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name); if (!cmp) { printf("add_key: key %s already exists!\n",name); FREE(newlf); @@ -2061,7 +2464,7 @@ printf("add_key: new index!\n"); #endif ALLOC(newlf, 8 + 8, 1); - newlf->no_keys = 1; + newlf->no_keys = 0 ; /* Will increment to 1 when filling in the offset later */ /* Use ID (lf, lh or li) we fetched from root node, so we use same as rest of hive */ newlf->id = hdesc->nkindextype; slot = 0; @@ -2076,20 +2479,21 @@ FREE(newli); return(NULL); } + key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */ newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4); newnk->id = 0x6b6e; - newnk->type = KEY_NORMAL; + newnk->type = KEY_NORMAL; /* Some versions use 0x1020 a lot.. */ newnk->ofs_parent = nkofs - 0x1004; newnk->no_subkeys = 0; - newnk->ofs_lf = 0; + newnk->ofs_lf = -1; newnk->no_values = 0; newnk->ofs_vallist = -1; newnk->ofs_sk = key->ofs_sk; /* Get parents for now. 0 or -1 here crashes XP */ newnk->ofs_classnam = -1; newnk->len_name = strlen(name); newnk->len_classnam = 0; - strcpy(newnk->keyname, name); + memcpy(newnk->keyname, name, newnk->len_name); if (newli) { /* Handle li */ @@ -2109,6 +2513,9 @@ free_block(hdesc,newnkofs); return(NULL); } + key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */ + newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4); + /* memcpy(hdesc->buffer + newliofs + 4, newli, 8 + 4*newli->no_keys); */ fill_block(hdesc, newliofs, newli, 8 + 4*newli->no_keys); @@ -2130,7 +2537,7 @@ } else if (newlf->id == 0x686c) { /* lh. XP uses this. hashes whole name */ for (i = 0,hash = 0; i < strlen(name); i++) { hash *= 37; - hash += toupper(name[i]); + hash += reg_touppertable[(unsigned char)name[i]]; } newlf->lh_hash[slot].hash = hash; } @@ -2143,6 +2550,10 @@ free_block(hdesc,newnkofs); return(NULL); } + + key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */ + newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4); + /* memcpy(hdesc->buffer + newlfofs + 4, newlf, 8 + 8*newlf->no_keys); */ fill_block(hdesc, newlfofs, newlf, 8 + 8*newlf->no_keys); @@ -2164,7 +2575,6 @@ FREE(newli); return(newnk); - } /* Delete a subkey from a key @@ -2193,6 +2603,11 @@ namlen = strlen(name); +#ifdef DKDEBUG + printf("del_key: deleting: <%s>\n",name); +#endif + + if (key->id != 0x6b6e) { printf("add_key: current ptr not nk\n"); return(1); @@ -2276,24 +2691,36 @@ #endif FREE(newlf); ALLOC(newlf, 8 + 8*oldlf->no_keys - 8, 1); +#ifdef DKDEBUG + printf("alloc newlf: %x\n",newlf); +#endif newlf->no_keys = oldlf->no_keys - 1; no_keys = newlf->no_keys; newlf->id = oldlf->id; /* Now copy old, checking where to delete */ for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) { + onkofs = oldlf->hash[o].ofs_nk; onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004); + if (slot == -1 && (onk->len_name == namlen) && !strncmp(name, onk->keyname, onk->len_name)) { slot = o; delnkofs = onkofs; delnk = onk; rimax = rislot; o++; } - newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk; - newlf->hash[n].name[0] = oldlf->hash[o].name[0]; - newlf->hash[n].name[1] = oldlf->hash[o].name[1]; - newlf->hash[n].name[2] = oldlf->hash[o].name[2]; - newlf->hash[n].name[3] = oldlf->hash[o].name[3]; + + if (n < newlf->no_keys) { /* Only store if not last index in old */ +#ifdef DKDEBUG + printf("del_key: n = %d, o = %d\n",n,o); +#endif + newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk; + newlf->hash[n].name[0] = oldlf->hash[o].name[0]; + newlf->hash[n].name[1] = oldlf->hash[o].name[1]; + newlf->hash[n].name[2] = oldlf->hash[o].name[2]; + newlf->hash[n].name[3] = oldlf->hash[o].name[3]; + } + } } /* else lh or lf */ @@ -2328,6 +2755,11 @@ FREE(newlf); return(1); } + key = (struct nk_key *)(hdesc->buffer + nkofs); + oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + + /* memcpy(hdesc->buffer + newlfofs + 4, ((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys); @@ -2360,8 +2792,14 @@ /* Update parent */ key->no_subkeys--; - if (ri) { - if (newlfofs == 0xfff) { + key = (struct nk_key *)(hdesc->buffer + nkofs); + oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + + if (ri) { + ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); /* In case realloc */ + + if (newlfofs == 0xfff) { *fullpath = 0; get_abs_path(hdesc, nkofs, fullpath, 480); @@ -2384,6 +2822,10 @@ FREE(newri); return(1); } + key = (struct nk_key *)(hdesc->buffer + nkofs); + oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + fill_block(hdesc, newriofs, newri, 8 + newri->no_lis * 4); free_block(hdesc, riofs + 0x1000); key->ofs_lf = newriofs - 0x1000; @@ -2458,6 +2900,7 @@ } del_allvalues(hdesc, nkofs); + key = (struct nk_key *)(hdesc->buffer + nkofs); del_key(hdesc, key->ofs_parent+0x1004, path); } @@ -2519,11 +2962,20 @@ int vofs, char *path, int type, int exact ) { int l; - void *keydataptr; + void *keydataptr, *addr; + struct db_key *db; + int copylen, blockofs, blocksize, restlen, point, i, list, parts; if (!kv) return(0); + + l = get_val_len(hdesc, vofs, path, exact); if (l == -1) return(0); /* error */ + // printf("put_buf2val: l = %d\n",l); + + // printf("put_buf2val: %s, kv len = %d, l = %d\n",path,kv->len,l); + + if (kv->len != l) { /* Realloc data block if not same size as existing */ if (!alloc_val_data(hdesc, vofs, path, kv->len, exact)) { printf("put_buf2val: %s : alloc_val_data failed!\n",path); @@ -2532,10 +2984,44 @@ } keydataptr = get_val_data(hdesc, vofs, path, type, exact); - if (!keydataptr) return(0); /* error */ + if (!keydataptr) { + printf("put_buf2val: %s : get_val_data failed!\n",path); + return(0); /* error */ + } + + + + if (kv->len > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */ + db = (struct db_key *)keydataptr; + if (db->id != 0x6264) abort(); + parts = db->no_part; + list = db->ofs_data + 0x1004; + printf("put_buf2val: Long value: parts = %d, list = %x\n",parts,list); + + point = 0; + restlen = kv->len; + for (i = 0; i < parts; i++) { + blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000; + blocksize = -get_int(hdesc->buffer + blockofs) - 8; - memcpy(keydataptr, &kv->data, kv->len); + /* Copy this part, up to size of block or rest lenght in last block */ + copylen = (blocksize > restlen) ? restlen : blocksize; + printf("put_buf2val: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize); + printf(" : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen); + + addr = (void *)&(kv->data) + point; + fill_block( hdesc, blockofs, addr, copylen); + + // debugit((char *)&(kr->data), l); + + point += copylen; + restlen -= copylen; + } + } else { + memcpy(keydataptr, &kv->data, kv->len); + } + hdesc->state |= HMODE_DIRTY; return(kv->len); @@ -2577,21 +3063,172 @@ * len: length of the string * return: the converted string as char* */ -char * convert_string(void *string, int len) +char * +string_regw2prog(void *string, int len) +{ + int i, k; + char *cstring; + + int out_len = 0; + for(i = 0; i < len; i += 2) + { + unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u; + if (v < 128) + out_len += 1; + else if(v < 0x800) + out_len += 2; + else + out_len += 3; + } + CREATE(cstring,char,out_len+1); + + for(i = 0, k = 0; i < len; i += 2) + { + unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u; + if (v < 128) + cstring[k++] = v; + else if(v < 0x800) { + cstring[k++] = 0xc0 | (v >> 6); + cstring[k++] = 0x80 | (v & 0x3f); + } else { + cstring[k++] = 0xe0 | (v >> 12); + cstring[k++] = 0x80 | ((v >> 6) & 0x3f); + cstring[k++] = 0x80 | (v & 0x3f); + } + } + cstring[out_len] = '\0'; + + return cstring; +} + +#if 0 // Not used at the moment +static char * +string_rega2prog(void *string, int len) { int i, k; - int reallen = len / 2; - char *cstring = (char *)malloc(reallen); + char *cstring; - for(i = 0, k = 0; i < len; i += 2, k++) + int out_len = 0; + for(i = 0; i < len; ++i) { - cstring[k] = ((char *)string)[i]; + unsigned v = ((unsigned char *)string)[i]; + if (v < 128) + out_len += 1; + else + out_len += 2; } - cstring[reallen - 1] = '\0'; + CREATE(cstring,char,out_len+1); + + for(i = 0, k = 0; i < len; ++i) + { + unsigned v = ((unsigned char *)string)[i]; + if (v < 128) + cstring[k++] = v; + else { + cstring[k++] = 0xc0 | (v >> 6); + cstring[k++] = 0x80 | (v & 0x3f); + } + } + cstring[out_len] = '\0'; return cstring; } +static void +string_prog2rega(char *string, int len) +{ + char *out = string; + unsigned char *in = (unsigned char*) string; + + for (;*in; ++in) { + if (!(*in & 0x80)) { + *out++ = *in; + } else if ((in[0] & 0xe0) == 0xc0 && in[1]) { + *out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f); + ++in; + } else if (in[1] && in[2]) { + /* assume 3 byte*/ + *out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f); + in += 2; + } + } + *out = 0; +} +#endif // Not used + + +static char * +string_prog2regw(void *string, int len, int *out_len) +{ + unsigned char *regw = (unsigned char*) malloc(len*2+2); + unsigned char *out = regw; + unsigned char *in = (unsigned char*) string; + + for (;len>0; ++in, --len) { + if (!(in[0] & 0x80)) { + *out++ = *in; + *out++ = 0; + } else if ((in[0] & 0xe0) == 0xc0 && len >= 2) { + *out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f); + *out++ = (in[0] & 0x1f) >> 2; + ++in, --len; + } else if (len >= 3) { + /* assume 3 byte*/ + *out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f); + *out++ = (in[0] & 0xf) << 4 | ((in[1] & 0x3f) >> 2); + in += 2; + len -= 2; + } + } + *out_len = out - regw; + out[0] = out[1] = 0; + return (char *) regw; +} + +static char * +quote_string(const char *s) +{ + int len = strlen(s); + const char *p; + char *dst, *out; + + for (p = s; *p; ++p) + if (*p == '\\' || *p == '\"') + ++len; + + dst = out = (char*) malloc(len + 1); + for (p = s; *p; ++p) { + if (*p == '\\' || *p == '\"') + *dst++ = '\\'; + *dst++ = *p; + } + *dst = 0; + return out; +} + +static void +export_bin(int type, char *value, int len, int col, FILE* file) +{ + int byte; + + if (type == REG_BINARY) { + fprintf(file, "hex:"); + col += 4; + } else { + fprintf(file, "hex(%x):", type); + col += 7; + } + byte = 0; + int start = (col - 2) / 3; + while (byte + 1 < len) { /* go byte by byte.. probably slow.. */ + fprintf(file, "%02x,", (unsigned char)value[byte]); + byte++; + if (!((byte + start) % 25)) fprintf(file, "\\\r\n "); + } + if (len) + fprintf(file, "%02x\r\n", (unsigned char)value[len - 1]); +} + /* * Exports the named subkey and its values to the given file. * @@ -2606,9 +3243,8 @@ int newofs; int count = 0; int countri = 0; - int len, byte; - char keyname[128]; - char path[1024]; + int len; + char *path = (char*) malloc(1024); char *value; struct nk_key *key; struct ex_data ex; @@ -2619,17 +3255,16 @@ newofs = trav_path(hdesc, nkofs, name, TPF_NK_EXACT); if(!newofs) { - printf("Key '%s' not found!\n", name); + printf("export_subkey: Key '%s' not found!\n", name); + free(path); return; } nkofs = newofs + 4; // get the key key = (struct nk_key *)(hdesc->buffer + nkofs); - strncpy(keyname, key->keyname, key->len_name); - keyname[key->len_name] = '\0'; - printf("Exporting key '%s' with %d subkeys and %d values...\n", - keyname, key->no_subkeys, key->no_values); + printf("Exporting key '%.*s' with %d subkeys and %d values...\n", + key->len_name, key->keyname, key->no_subkeys, key->no_values); *path = 0; get_abs_path(hdesc, nkofs, path, 1024); @@ -2642,17 +3277,46 @@ { while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) { + int col = 0; + char *name = quote_string(vex.name); + + /* print name */ + if (!name[0]) { + fprintf(file, "@="); + free(name); + name = str_dup("@"); + col += 2; + } else { + fprintf(file, "\"%s\"=", name); + col += strlen(name) + 3; + } + if(vex.type == REG_DWORD) { - fprintf(file, "\"%s\"=dword:%08x\r\n", vex.name, vex.val); + fprintf(file, "dword:%08x\r\n", vex.val); } else if(vex.type == REG_SZ) { - value = (char *)get_val_data(hdesc, nkofs, vex.name, vex.type, TPF_VK_EXACT); - len = get_val_len(hdesc, nkofs, vex.name, TPF_VK_EXACT); - - fprintf(file, "\"%s\"=\"%s\"", vex.name, convert_string(value, len)); - fprintf(file, "\r\n"); + char *val, *quoted; + value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS); + len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS); + + val = string_regw2prog(value, len); + /* dump as binary if invalid characters are embedded */ + if (strchr(val, 0xa) || strchr(val, 0xd)) + { + free(val); + //if (len >= 2 && value[len-2] == 0 && value[len-1] == 0) len -= 2; + export_bin(vex.type, value, len, col, file); + } + else + { + quoted = quote_string(val); + free(val); + fprintf(file, "\"%s\"", quoted); + free(quoted); + fprintf(file, "\r\n"); + } } else { @@ -2668,24 +3332,14 @@ NOTE: Exception: type = REG_BINARY starts like this: "valuename"=hex:xx,xx,xx... */ - value = (char *)get_val_data(hdesc, nkofs, vex.name, vex.type, TPF_VK_EXACT); - len = get_val_len(hdesc, nkofs, vex.name, TPF_VK_EXACT); + value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS); + len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS); - if (vex.type == REG_BINARY) { - fprintf(file, "\"%s\"=hex:", vex.name); - } else { - fprintf(file, "\"%s\"=hex(%x):", vex.name, vex.type); - } - byte = 0; - while (byte < len) { /* go byte by byte.. probably slow.. */ - fprintf(file, "%02x,", (unsigned char)value[byte]); - byte++; - if (!(byte % 20)) fprintf(file, "\\\r\n "); - } - fprintf(file, "%02x\r\n", (unsigned char)value[byte]); + export_bin(vex.type, value, len, col, file); } FREE(vex.name); + free(name); } } @@ -2700,6 +3354,7 @@ FREE(ex.name); } } + free(path); } /* @@ -2723,18 +3378,605 @@ file = fopen(filename, "w"); if(!file) { - printf("Cannot open file '%s'. %s (%d).\n", filename, strerror(errno), + printf("export: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno), errno); return; } printf("Exporting to file '%s'...\n", filename); - fprintf(file, "Windows Registry Editor Version 5.00\r\n\r\n"); + fprintf(file, "Windows Registry Editor Version 5.00\r\n"); export_subkey(hdesc, nkofs, name, prefix, file); + fprintf(file,"\r\n"); /* Must end file with an empty line, windows does that */ + fclose(file); } +/* ================================================================ */ + +/* Import from .reg file routines */ + +#define MAXLINE 20000 + +/* Wide character fgetsw() may not be available on all small libraries.. + * so.. roll our own fgets() that handles wide if needed + */ + +char *my_fgets(char *s, char *w, int max, FILE *file, int wide) +{ + + int i = 0; + char prev = 1; + char c = 1; + + + while (c != '\n' && !feof(file) && max) { + c = (char)fgetc(file); + /* printf("char = %c\n",c); */ + if (!c && (!prev && !wide) ) break; /* Stop on 1 (or 2 if wide) null */ + prev = c; + if (c != '\r') { + *(s+i) = c; + *(w+i) = c; + i++; + } + max--; + } + *(s+i) = 0; + *(s+i+1) = 0; + *(w+i) = 0; + *(w+i+1) = 0; + + if (wide) { /* Convert to C string, de widing it.. */ + cheap_uni2ascii(w, s, i); + // printf("cheap returns len = %d : %s\n",strlen(s), s); + + fgetc(file); /* Skip second byte of CR/LF termination */ + } + + // printf("my_fgets returning :\n"); + //hexdump(w, 0, i, 1); + //printf("====== hexdump end\n"); + return(s); + +} + + +/* Read one line, while skipping blank lines, also checks for = + * line = line buffer + * file = you know.. + * assigner - left part of value assignemt (before =) + * value - right part of assignment (after =) + * value = NULL if [KEY] line. + * assigner = NULL if value continuation line + * Returns total lenght of line + */ + + +#undef GETLINE_DEBUG + +int get_line(char s[], char w[], FILE *file, char **assigner, char **value, int wide) +{ + int l,q; + char *b; /* Start of line after spaces */ + char *c; + + *assigner = NULL; + *value = NULL; + + do { + + s[0] = 0; + my_fgets(s, w, MAXLINE, file, wide); + s[MAXLINE] = 0; + b = s; + + l = strlen(s); + +#ifdef GETLINE_DEBUG + printf("get_line: read line len %d : %s\n",l,s); +#endif + + if (l == 0) { + if (feof(file)) return 0; + break; + } + + while (isspace(*b)) b++; + c = b; + while (*c && *c != '\n' && *c != '\r') c++; + *c = 0; /* Terminate with null not cr/lf */ + + + if (!*b) continue; /* Blank line */ + + l = strlen(s); + +#ifdef GETLINE_DEBUG + printf("get_line: stripped line len %d : %s\n",l,s); +#endif + + + c = b; + + if (*b == '[') { /* Key line starts with [ */ +#ifdef GETLINE_DEBUG + printf("get_line: key line..\n"); +#endif + while (*c && (*c != ']')) c++; + if (!*c) { + printf("get_line: WARNING: un-terminated key line..\n"); + } + *c = 0; + *assigner = b+1; + *value = NULL; + return(l); + } + + q = 0; + while (*c) { + /* printf(" char = %c : q = %d\n",*c,q); */ + if (*c == '"') q ^= 1; /* Flip quote indicator */ + if (*c == '=' && !q) { /* Found = outside quotes */ + *c = 0; + *assigner = b; + *value = c + 1; +#ifdef GETLINE_DEBUG + printf("get_line: value line\n"); +#endif + return(l); + } + c++; + } + /* At this point we don't have a = outside quotes, so probably a value cont line */ + *assigner = NULL; + *value = b; +#ifdef GETLINE_DEBUG + printf("get_line: cont line\n"); +#endif + return(l); + + } while (!feof(file)); + + return(l); +} + + +/* Wide strlen */ +int wide_strlen(char *s) +{ + + int l = 0; + + while ( *(s+l) || *(s+l+1) ) l += 2; + + return(l); + +} + + + +/* De-quote a string (removing quotes first and last) + * Does nothing if no quoutes + * String MUST be null-terminated + */ + +char *dequote(char *s) +{ + int l; + + if (*s != '"') return(s); /* No first quote, then don't change anything, even at end */ + + l = strlen(s); + + if ( *(s+l-1) == '"' ) { + *(s+l-1) = 0; + } + + return(s+1); +} + +/* de-escape a string, handling \ backslash + * s = string buffer, WILL BE CHANGED + * wide = true to make it handle wide characters + * returns new lenght + */ + +int de_escape(char *s, int wide) +{ + int src = 0; + int dst = 0; + + if (wide) { + while ( *(s + src) || *(s + src +1)) { + if ( *(s + src) == '\\' && *(s + src + 1) == 0) src += 2; /* Skip over backslash */ + *(s + dst) = *(s + src); + *(s + dst + 1) = *(s + src + 1); + dst += 2; + src += 2; + } + *(s + dst) = 0; + *(s + dst + 1) = 0; + dst += 2; + } else { + while ( *(s + src) ) { + if ( *(s + src) == '\\' ) src++; + *(s + dst) = *(s + src); + dst++; + src++; + } + *(s + dst) = 0; + dst++; + } + + return(dst); +} + + + + +/* Parse the value to be assigned + * s = string to parse + * bufptr = pointer to buffer pointer for binary data (will be allocated). + * First byte of buffer is value type. + * returns lenght + * +"stringvalue"="this is a string" +"binaryvalue"=hex:11,22,33,aa,bb,cc,12,34,01,02,ab,cd,ef,be,ef +"dwordvalue"=dword:12345678 +"qwordvalue"=hex(b):ef,cd,ab,89,67,45,23,01 +"multistringvalue"=hex(7):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,31,00,00,\ + 00,73,00,74,00,72,00,69,00,6e,00,67,00,20,00,32,00,00,00,73,00,74,00,72,00,\ + 69,00,6e,00,67,00,20,00,33,00,00,00,00,00 +"expstringvalue"=hex(2):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,77,00,69,00,\ + 74,00,68,00,20,00,25,00,73,00,20,00,65,00,78,00,70,00,61,00,6e,00,73,00,69,\ + 00,6f,00,6e,00,00,00 + * + */ + +int parse_valuestring(char *s, char *w, int len, int wide, struct keyval **kvptr) +{ + unsigned int dword; + int type = -1; + int i; + int quote; + int strstart; + uint8_t byte; + uint8_t *array; + char *widebuf = NULL; + struct keyval *kv = NULL; + + + // printf("parse_val: input string: <%s>\n",s); + + if (!strncmp(s,"dword",4)) { /* DWORD */ + sscanf(s,"dword:%x",&dword); + // printf("parse_vals: dword is %x\n",dword); + type = REG_DWORD; + len = 4; + ALLOC(kv,1,len + 8); + kv->data = dword; + + } else if (!strncmp(s,"hex",3)) { /* Hex string */ + if (!sscanf(s,"hex(%x):",&type)) type = REG_BINARY; + + // printf("parse_vals: hex type is %d\n",type); + + while (*s && *s != ':') s++; /* Move up to : */ + s++; + len = strlen(s); + if (len > 0) len = len / 3 + 1; /* 3 characters per byte */ + + // printf("parse_vals: hex byte count %d\n", len); + + ALLOC(kv,1,len + 8); + array = (uint8_t *)&kv->data; + + for (i = 0; i < len; i++) { + if (!sscanf(s,"%hhx",&byte)) { + fprintf(stderr,"parse_values: hex string parse error: %s\n",s); + abort(); + } + // printf("parse_vals: adding byte: %02x\n",byte); + *(array+i) = byte; + s += 3; + } + + } else { /* Assume it simply is a string */ + type = REG_SZ; + + if (wide) { /* File is wide, find string limits and de-escape it, then copy in as wide */ + + quote = 0; + i = 0; + strstart = 0; + while ( *(w+i) ) { + if ( *(w+i) == '"' && *(w+i+1) == 0) quote ^= 1; + if (!quote && !strstart && ( *(w+i) == '=' && *(w+i+1) == 0) ) { + strstart = i + 4; /* Skip past start quote */ + } + i += 2; + } + if ( *(w+i-2) == '"' && *(w+i-1) == 0) { /* Remove end quoute */ + *(w+i-2) = 0; + *(w+i-1) = 0; + i -= 2; + } + i += 2; + + + // hexdump(w+strstart,0,i-strstart,1); + + len = de_escape(w + strstart, wide); + + // hexdump(w+strstart,0,len,1); + + // printf("wide string: i = %d, strstart = %d, len = %d\n",i,strstart,len); + + ALLOC(kv,1,len + 8); + + memcpy(&kv->data,w + strstart, len); + + } else { /* File is not wide, so we must widen string before putting into registry */ + + len = strlen(s); + // printf("parse_vals: len %d string <%s>\n",len,s); + len = de_escape(s, 0); + // printf("parse_vals: after de-escape len %d string <%s>\n",len,s); + + widebuf = string_prog2regw(s, strlen(s), &len); + len += 2; /* Also store the terminating NULLs */ + // printf("parse_vals: len after wide expansion: %d\n",len); + + ALLOC(kv,1,len + 8); + memcpy(&kv->data,widebuf,len); + + FREE(widebuf); + } + + + } + + if (kv) { + kv->len = len; + *kvptr = kv; + } + + return(type); + +} + + + + +/* Main import routine + * Only reading into one hive at a time supported. + * Will support 8 bit or 16 bit (wide) character .reg file. + * Windows regedit.exe usually generates 16 bit. + * NOTE: Not a lot of sanity checking, a broken input file may end in strange behaviour or corrupt registry! + * hdesc - Loaded hive + * filename - (path)name of .reg file to read + * prefix - HKEY_ + */ + +void import_reg(struct hive *hdesc, char *filename, char *prefix) +{ + + FILE *file; + char line[MAXLINE+2]; + char wline[MAXLINE+2]; /* Wide buffer */ + int l, plen; + char *assigner = NULL; + char *value = NULL; + int wide = 0; + int c,wl; + void *valbuf; + char *valname = NULL; + char *plainname = NULL; + char *valstr, *key, *walstr = NULL; + int nk, prevnk; /* offsets to nk */ + int type,oldtype; + struct keyval *valbinbuf = NULL; + int numkeys = 0; + int numkeyadd = 0; + int numtotvals = 0; + int numkeyvals = 0; + int bailout = 0; + + plen = strlen(prefix); + + file = fopen(filename, "r"); + if(!file) + { + fprintf(stderr,"import_reg: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno), + errno); + return; + } + + c = fgetc(file); + + if (c == 0xff) { /* Wide characters file */ + fprintf(stderr,"import_reg: WARNING: Wide character (16 bit) file..\n" + "import_reg: WARNING: Implementation is not 100%% accurate, some things may not import correctly!\n"); + c = fgetc(file); /* Get second wide indicator character */ + wide = 1; + } else { + ungetc(c, file); + } + + line[0] = 0; + my_fgets(line, wline, MAXLINE, file, wide); + line[MAXLINE] = 0; + + + if (strncmp("Windows Registry Editor",line,23)) { + fprintf(stderr,"import_reg: ERROR: Windows Registry Editor signature missing on first line\n"); + fclose(file); + return; + } + + do { + + l = get_line(line, wline, file, &assigner, &value, wide); + + if (!feof(file) && !assigner && value) { + // printf("import_reg: value continuation line: %s\n",value); + l = strlen(value); + if ( *(value+l-1) == '\\' ) { /* Strip off cont character */ + *(value+l-1) = 0; + l--; + } + value = dequote(value); + l = strlen(value); + valstr = realloc(valstr, strlen(valstr) + l + 3); + if (valstr == NULL) { + perror("import_reg: realloc(valstr)"); + abort(); + } + strcat(valstr, value); + // printf("import_reg: build value string: %s\n",valstr); + if (wide) { + wl = wide_strlen(wline); + walstr = realloc(walstr, wide_strlen(walstr) + wl + 4); + if (walstr == NULL) { + perror("import_reg: realloc(valstr)"); + abort(); + } + memcpy(walstr + wl, wline, wl); + } + + + + } else { /* End continuation, store built up value */ + if (valname) { + // printf("import_reg: end of value %s, result string: %s\n\n",valname,valstr); + + type = parse_valuestring(valstr, walstr, l, wide, &valbinbuf); + + // printf("import_reg: got value type = %d\n",type); + // printf("import_reg: data lenght = %d\n",(*valbinbuf).len); + + VERBF(hdesc," Value <%s> of type %d length %d",valname,type,(*valbinbuf).len); + + oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT); + + + if (oldtype == -1) { + // printf("Value <%s> not found, creating it new\n",valname); + plainname = str_dup(valname); + de_escape(plainname,0); + // printf("de-escaped to <%s> creating it new\n",plainname); + add_value(hdesc, nk + 4, plainname, type); + oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT); + FREE(plainname); + VERB(hdesc," [CREATED]"); + + } + + if (oldtype != type) { + fprintf(stderr,"ERROR: import_reg: unable to change value <%s>, new type is %d while old is %d\n",valname,type,oldtype); + bailout = 1; + } else { + + VERB(hdesc,"\n"); + put_buf2val(hdesc, valbinbuf, nk + 4, valname, type, TPF_VK_ABS|TPF_EXACT); + + numkeyvals++; + numtotvals++; + + FREE(valbinbuf); + FREE(valstr); + FREE(valname); + FREE(walstr); + } + } + } + + + if (assigner && !value) { /* Its a key line */ + //printf("import_reg: read key name: %s\n",assigner); + if ( !strncmp(assigner,prefix,plen)) { /* Check and strip of prefix of key name */ + assigner += plen; + } else { + fprintf(stderr,"import_reg: WARNING: found key <%s> not matching prefix <%s>\n",assigner,prefix); + abort(); + } + + if (numkeys) { + if (hdesc->state & HMODE_VERBOSE) + printf("--- END of key, with %d values\n",numkeyvals); + else + printf(" with %d values.\n",numkeyvals); + numkeyvals = 0; + } + + printf("--- Import KEY <%s> ",assigner); + numkeys++; + + key = strtok(assigner,"\\"); + prevnk = hdesc->rootofs; + while (key) { + nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT); + if (!nk) { + if (!add_key(hdesc, prevnk + 4, key)) { + fprintf(stderr,"\nERROR: import_reg: failed to add (sub)key <%s>\n",key); + bailout = 1; + } else { + printf(" [added <%s>] ",key); + nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT); + numkeyadd++; + } + } + prevnk = nk; + key = strtok(NULL,"\\"); + } + fflush(stdout); + VERB(hdesc,"\n"); + } + + + if (assigner && value) { + // printf("import_reg: value assignment line: %s = %s\n",assigner,value); + valname = str_dup(dequote(assigner)); + if (wide) { + FREE(walstr); + ALLOC(walstr, 1, wide_strlen(wline)); + memcpy(walstr, wline, wide_strlen(wline)); + } + + l = strlen(value); + if ( *(value+l-1) == '\\' ) { /* Strip off cont character */ + *(value+l-1) = 0; + l--; + } + value = dequote(value); + valstr = str_dup(value); + valbuf = NULL; + //printf("import_reg: val name = %s\n",valname); + //printf("import_reg: val str = %s\n",valstr); + + } + + + } while (!feof(file) && !bailout); + + + printf("\nEND OF IMPORT, file <%s>, operation %s!\n", filename, (bailout ? "FAILED" : "SUCCEEDED")); + printf("%d keys\n",numkeys); + printf("%d new keys added\n",numkeyadd); + printf("%d values total\n\n",numtotvals); + fclose(file); + + if (bailout) hdesc->state &= ~HMODE_DIRTY; /* Don't save if error. Or should we? */ + +} + + + + + + + /* ================================================================ */ @@ -2743,7 +3985,7 @@ void closeHive(struct hive *hdesc) { - printf("closing hive %s\n",hdesc->filename); + // printf("closing hive %s\n",hdesc->filename); if (hdesc->state & HMODE_OPEN) { close(hdesc->filedesc); } @@ -2753,10 +3995,34 @@ } + +/* Compute checksum of REGF header page + * hdesc = hive + * returns checksum value, 32 bit int + */ + +int32_t calc_regfsum(struct hive *hdesc) +{ + int32_t checksum = 0; + struct regf_header *hdr; + int i; + + hdr = (struct regf_header *) hdesc->buffer; + + for (i = 0; i < 0x1fc/4; ++i) + checksum ^= ((int32_t *) hdr)[i]; + + return(checksum); + +} + + + /* Write the hive back to disk (only if dirty & not readonly */ int writeHive(struct hive *hdesc) { int len; + struct regf_header *hdr; if (hdesc->state & HMODE_RO) return(0); if ( !(hdesc->state & HMODE_DIRTY)) return(0); @@ -2771,6 +4037,13 @@ /* Seek back to begginning of file (in case it's already open) */ lseek(hdesc->filedesc, 0, SEEK_SET); + /* compute new checksum */ + + hdr = (struct regf_header *) hdesc->buffer; + + hdr->checksum = calc_regfsum(hdesc); + + len = write(hdesc->filedesc, hdesc->buffer, hdesc->size); if (len != hdesc->size) { fprintf(stderr,"writeHive: write of %s failed: %s.\n",hdesc->filename,strerror(errno)); @@ -2781,6 +4054,8 @@ return(0); } +#undef LOAD_DEBUG + struct hive *openHive(char *filename, int mode) { @@ -2788,14 +4063,15 @@ int fmode,r,vofs; struct stat sbuf; uint32_t pofs; - /* off_t l; */ + int32_t checksum; char *c; struct hbin_page *p; struct regf_header *hdr; struct nk_key *nk; struct ri_key *rikey; + int verbose = (mode & HMODE_VERBOSE); - int trace = (mode & HMODE_TRACE); + int trace = (mode & HMODE_TRACE) ? 1 : 0; CREATE(hdesc,struct hive,1); @@ -2851,9 +4127,22 @@ hdr = (struct regf_header *)hdesc->buffer; if (hdr->id != 0x66676572) { - printf("openHive(%s): File does not seem to be a registry hive!\n",filename); + fprintf(stderr,"openHive(%s): File does not seem to be a registry hive!\n",filename); return(hdesc); } + + checksum = calc_regfsum(hdesc); + +#ifdef LOAD_DEBUG + printf("openhive: calculated checksum: %08x\n",checksum); + printf("openhive: file REGF checksum: %08x\n",hdr->checksum); +#endif + if (checksum != hdr->checksum) { + 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); @@ -2868,7 +4157,7 @@ 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("load_hive: DEBUG: BIG ROOT!\n"); + printf("openHive: DEBUG: BIG ROOT!\n"); rikey = (struct ri_key *)(hdesc->buffer + rikey->hash[0].ofs_li + 0x1004); hdesc->nkindextype = rikey->id; } @@ -2883,14 +4172,14 @@ hdesc->nkindextype & 0xff, hdesc->nkindextype >> 8); } else { - printf("load_hive: WARNING: ROOT key does not seem to be a key! (not type == nk)\n"); + fprintf(stderr,"openHive: WARNING: ROOT key does not seem to be a key! (not type == nk)\n"); } - - while (pofs < hdesc->size) { + while (pofs < hdr->filesize + 0x1000) { /* Loop through hbins until end according to regf header */ #ifdef LOAD_DEBUG - if (trace) hexdump(hdesc->buffer,pofs,pofs+0x20,1); + int htrace = 1; + // if (htrace) hexdump(hdesc->buffer,pofs,pofs+0x20,1); #endif p = (struct hbin_page *)(hdesc->buffer + pofs); if (p->id != 0x6E696268) { @@ -2898,34 +4187,37 @@ break; } hdesc->pages++; -#ifdef LOAD_DEBUG - if (trace) printf("\n###### Page at 0x%0lx has size 0x%0lx, next at 0x%0lx ######\n",pofs,p->len_page,p->ofs_next); -#endif + + if (verbose) printf("###### Page at 0x%0x ofs_self 0x%0x, size (delta ofs_next) 0x%0x ######\n", + pofs,p->ofs_self,p->ofs_next); + if (p->ofs_next == 0) { -#ifdef LOAD_DEBUG - if (trace) printf("openhive debug: bailing out.. pagesize zero!\n"); -#endif + fprintf(stderr,"openHive: ERROR: Page at 0x%x has size zero! File may be corrupt, or program has a bug\n",pofs); return(hdesc); } -#if 0 - if (p->len_page != p->ofs_next) { -#ifdef LOAD_DEBUG - if (trace) printf("openhive debug: len & ofs not same. HASTA!\n"); -#endif - exit(0); - } -#endif - vofs = pofs + 0x20; /* Skip page header */ -#if 1 + vofs = pofs + 0x20; /* Skip page header, and run through blocks in hbin */ + while (vofs-pofs < p->ofs_next && vofs < hdesc->size) { vofs += parse_block(hdesc,vofs,trace); } -#endif + pofs += p->ofs_next; + + } /* hbin loop */ + + + hdesc->endofs = hdr->filesize + 0x1000; + hdesc->lastbin = pofs - p->ofs_next; /* Compensate for loop that added at end above */ + + if (verbose) { + printf("Last HBIN at offset : 0x%x\n",hdesc->lastbin); + printf("First non-HBIN page offset: 0x%x\n",hdesc->endofs); + 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); @@ -2934,13 +4226,14 @@ /* So, let's guess what kind of hive this is, based on keys in its root */ hdesc->type = HTYPE_UNKNOWN; + if (trav_path(hdesc, 0, "\\SAM", 0)) hdesc->type = HTYPE_SAM; else if (trav_path(hdesc, 0, "\\ControlSet", 0)) hdesc->type = HTYPE_SYSTEM; else if (trav_path(hdesc, 0, "\\Policy", 0)) hdesc->type = HTYPE_SECURITY; else if (trav_path(hdesc, 0, "\\Microsoft", 0)) hdesc->type = HTYPE_SOFTWARE; if (verbose) printf("Type of hive guessed to be: %d\n",hdesc->type); - return(hdesc); + return(hdesc); } diff -Nru chntpw-0.99.6/ntreg.h chntpw-0.99.6.110511/ntreg.h --- chntpw-0.99.6/ntreg.h 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/ntreg.h 2011-05-11 19:33:56.000000000 +0000 @@ -6,7 +6,7 @@ ***** * * NTREG - Window registry file reader / writer library - * Copyright (c) 1997-2008 Petter Nordahl-Hagen. + * Copyright (c) 1997-2011 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 @@ -30,7 +30,16 @@ #define KEY_ROOT 0x2c /* Type ID of ROOT key node */ #define KEY_NORMAL 0x20 /* Normal nk key */ -#define ABSPATHLEN 2048 +#define ABSPATHLEN 4096 + + +/* hbin page size. hbins are minimum this, and always multiple of this */ +#define HBIN_PAGESIZE 0x1000 +/* Hive filesize seems to always be multiple of this */ +#define REGF_FILEDIVISOR 0x40000 + +/* Larger than this, and values seems split into several blocks */ +#define VAL_DIRECT_LIMIT 0x3fd0 /* Datatypes of the values in the registry */ @@ -60,18 +69,18 @@ struct regf_header { int32_t id; /* 0x00000000 D-Word ID: ASCII-"regf" = 0x66676572 */ - int32_t unknown1; /* 0x00000004 D-Word ???? */ + int32_t unknown1; /* 0x00000004 D-Word ???? Mount count */ int32_t unknown2; /* 0x00000008 D-Word ???? Always the same value as at 0x00000004 */ - char timestamp[8]; /* 0x0000000C Q-Word last modify date in WinNT date-format */ + char timestamp[8]; /* 0x0000000C Q-Word last modify date in WinNT date-format */ int32_t unknown3; /* 0x00000014 D-Word 1 */ int32_t unknown4; /* 0x00000018 D-Word 3 - probably version #. 2 in NT3.51 */ int32_t unknown5; /* 0x0000001C D-Word 0 */ int32_t unknown6; /* 0x00000020 D-Word 1 */ int32_t ofs_rootkey; /* 0x00000024 D-Word Offset of 1st key record */ - int32_t filesize; /* 0x00000028 D-Word Size of the data-blocks (Filesize-4kb) */ + int32_t filesize; /* 0x00000028 D-Word Offset of first non-used data at end of file */ int32_t unknown7; /* 0x0000002C D-Word 1 */ - char name[0x1fc-0x2c]; /* Seems like the hive's name is buried here, max len unknown */ - int32_t checksum; /* 0x000001FC D-Word Sum of all D-Words from 0x00000000 to 0x000001FB */ + char name[0x1fc-0x30]; /* 0x00000030 Seems like the hive's name is buried here, max len unknown */ + int32_t checksum; /* 0x000001FC D-Word Xor sum of all D-Words from 0x00000000 to 0x000001FB */ }; /* The page header, I don't know if the 14 "dummy" bytes has a meaning, @@ -81,21 +90,23 @@ struct hbin_page { int32_t id; /* 0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268 */ - int32_t ofs_from1; /* 0x0004 D-Word Offset from the 1st hbin-Block */ - int32_t ofs_next; /* 0x0008 D-Word Offset to the next hbin-Block (from THIS ONE) */ - char dummy1[14]; - int32_t len_page; /* 0x001C D-Word Block-size??? Don't look like it, - I only use the next-offset in this program */ - char data[1]; /* 0x0020 First data block starts here */ + int32_t ofs_self; /* 0x0004 D-Word Offset to itself, could be for sanity check */ + int32_t ofs_next; /* 0x0008 D-Word Relative offset to next hbin (practically length of this one) */ + char dummy1[14]; /* 0x14 to 0x001b may be timestamp in some windows versions, at least in first hbin */ + int32_t len_page; /* 0x001C D-Word Block-size??? Don't look like it. Most often zero. */ + + int32_t firstlink; /* 0x0020 First data block likage */ + /* char data[1]; 0x0020 First data block starts here */ }; -/* Minimum block size utilized at end of block - * seem to be either 8 or 16, less than this - * is only filled with garbage. (usually 0xB2 0xB2 ..) +/* Minimum block size utilized at end of hbin + * Make routines accept 0 size block when at end */ #define HBIN_ENDFILL 0 + + /* Security descriptor. I know how it's linked, but don't know how the real security data is constructed, it may as well be like the higher level security structs defined by MS in its @@ -164,6 +175,21 @@ }; +/* Indirect pointer list for value data, vk points to this instead of values data directly + * Seems to be used when value data is large, maybe larger than 3-4k. + */ +struct db_key { + + short id; /* 0x0000 Word ID: ASCII-"li" = 0x6462 */ + short no_part; /* 0x0002 Word number of data parts */ + /* 0x0004 ???? Pointers to data */ + int32_t ofs_data; /* 0x0000 D-Word Offset to list of data blocks */ + /* Something else seems to follow here, 4 bytes at least */ + /* and why not list the data block in here ???? why make another list?? */ +}; + + + /* This is a list of pointers to struct li_key, ie * an extention record if many li's. * This happens in NT4&5 when the lf hashlist grows larger @@ -188,8 +214,7 @@ /* This is the value descriptor. * If the sign bit (31st bit) in the length field is set, the value is * stored inline this struct, and not in a seperate data chunk - - * the data then seems to be in the type field, and maybe also - * in the flag and dummy1 field if -len > 4 bytes + * the data itself is then in the ofs_data field, happens for DWORD all the time * If the name size == 0, then the struct is probably cut short right * after the val_type or flag. * The flag meaning is rather unknown. @@ -202,7 +227,8 @@ int32_t len_data; /* 0x0004 D-Word length of the data */ int32_t ofs_data; /* 0x0008 D-Word Offset of Data */ int32_t val_type; /* 0x000C D-Word Type of value */ - short flag; /* 0x0010 Word Flag */ + short flag; /* 0x0010 Word Flag + 0x1 ANSI encoding */ short dummy1; /* 0x0012 Word Unused (data-trash) */ char keyname[1]; /* 0x0014 ???? Name */ @@ -220,18 +246,23 @@ /* Offset Size Contents */ short id; /* 0x0000 Word ID: ASCII-"nk" = 0x6B6E */ short type; /* 0x0002 Word for the root-key: 0x2C, otherwise 0x20 */ + /* 0x20 seems a flag for ANSI encoding */ + /* 0x1000 is used in some places in Vista and newer */ char timestamp[12]; /* 0x0004 Q-Word write-date/time in windows nt notation */ int32_t ofs_parent; /* 0x0010 D-Word Offset of Owner/Parent key */ int32_t no_subkeys; /* 0x0014 D-Word number of sub-Keys */ - char dummy1[4]; + int32_t dummy1; int32_t ofs_lf; /* 0x001C D-Word Offset of the sub-key lf-Records */ - char dummy2[4]; + int32_t dummy2; int32_t no_values; /* 0x0024 D-Word number of values */ int32_t ofs_vallist; /* 0x0028 D-Word Offset of the Value-List */ int32_t ofs_sk; /* 0x002C D-Word Offset of the sk-Record */ int32_t ofs_classnam; /* 0x0030 D-Word Offset of the Class-Name */ - char dummy3[16]; - int32_t dummy4; /* 0x0044 D-Word Unused (data-trash) */ + int32_t dummy3; /* 0x0034 unknown some of these may be used by vista */ + int32_t dummy4; /* 0x0038 unknown and newer ?? */ + int32_t dummy5; /* 0x003c unknown */ + int32_t dummy6; /* 0x0040 unknown */ + int32_t dummy7; /* 0x0044 unknown */ short len_name; /* 0x0048 Word name-length */ short len_classnam; /* 0x004A Word class-name length */ char keyname[1]; /* 0x004C ???? key-name */ @@ -269,9 +300,12 @@ /* Types to trav_path() */ #define TPF_NK 0 #define TPF_VK 1 +#define TPF_ABS 64 #define TPF_EXACT 128 +#define TPF_VK_SHORT 256 /* To get type field instead of data field, used in SAM */ #define TPF_NK_EXACT (TPF_NK | TPF_EXACT) #define TPF_VK_EXACT (TPF_VK | TPF_EXACT) +#define TPF_VK_ABS (TPF_VK | TPF_ABS) /* Name is literal, not a path */ /* Hive open modes */ @@ -279,7 +313,9 @@ #define HMODE_RO 0x1 #define HMODE_OPEN 0x2 #define HMODE_DIRTY 0x4 -#define HMODE_NOALLOC 0x8 +#define HMODE_NOALLOC 0x8 /* Don't allocate new blocks */ +#define HMODE_NOEXPAND 0x10 /* Don't expand file with new hbin */ +#define HMODE_DIDEXPAND 0x20 /* File has been expanded */ #define HMODE_VERBOSE 0x1000 #define HMODE_TRACE 0x2000 @@ -290,6 +326,8 @@ #define HTYPE_SECURITY 3 #define HTYPE_SOFTWARE 4 + + /* Hive definition, allocated by openHive(), dealloc by closeHive() * contains state data, must be passed in all functions */ @@ -304,8 +342,10 @@ int unuseblk; /* Total # of unused blocks */ int usetot; /* total # of bytes in useblk */ int unusetot; /* total # of bytes in unuseblk */ - int size; /* Hives size (filesise) in bytes */ + int size; /* Hives size (filesize) in bytes, incl regf header */ int rootofs; /* Offset of root-node */ + int lastbin; /* Offset to last HBIN */ + int endofs; /* Offset of first non HBIN page, we can expand from here */ short nkindextype; /* Subkey-indextype the root key uses */ char *buffer; /* Files raw contents */ }; @@ -386,8 +426,19 @@ void rdel_keys(struct hive *hdesc, char *path, int nkofs); struct keyval *get_class(struct hive *hdesc, int curnk, char *path); -/* From edlib,c */ +int add_bin(struct hive *hdesc, int size); + +void import_reg(struct hive *hdesc, char *filename, char *prefix); + +int de_escape(char *s, int wide); + +char *string_regw2prog(void *string, int len); + + +/* From edlib.c */ void regedit_interactive(struct hive *hive[], int no_hives); +void cat_dpi(struct hive *hdesc, int nkofs, char *path); + #endif diff -Nru chntpw-0.99.6/reged.c chntpw-0.99.6.110511/reged.c --- chntpw-0.99.6/reged.c 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/reged.c 2011-05-11 19:33:56.000000000 +0000 @@ -1,17 +1,19 @@ /* - * reged.c - Simple Registry Edit Utility for NT 3.51 4.0 5.0 5.1 6.0 registry hives. + * reged.c - Simple Registry Edit Utility for Windows registry hives. * - * This program is a command line utility to export of registry to .reg files - * Import from .reg is also planned - * Also, registry editor stuff should be moved into own library or something + * Frontend command line utility which uses registry library to: + * - Export (parts) of registry hive to .reg file + * - Import .reg file into registry hive + * - Do interactive registry edit * + * Changes: + * 2011 - may: Trace flags moved here. + * 2011 - apr: Added options for import and flags for safe modes.. * - * 2008-mar: First version. Call reg edit library. Export to .reg file. - * See HISTORY.txt for more detailed info on history. * ***** * - * Copyright (c) 1997-2007 Petter Nordahl-Hagen. + * Copyright (c) 1997-2010 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 @@ -36,7 +38,7 @@ #include "ntreg.h" -const char reged_version[] = "reged version 0.1 080526, (c) Petter N Hagen"; +const char reged_version[] = "reged version 0.1 110511, (c) Petter N Hagen"; /* Global verbosity flag */ @@ -50,13 +52,23 @@ void usage(void) { - printf("-x \n" - " Xport. Where for example is HKEY_LOCAL_MACHINE\n" - " is key to dump (recursively), . means all keys in hive\n" - "\n" + printf("\nModes:\n" + "-x \n" + " Xport. Where for example is HKEY_LOCAL_MACHINE\\SOFTWARE\n" + " is key to dump (recursively), \\ or \\\\ means all keys in hive\n" + " Only one .reg and one hive file supported at the same time\n" + "-I \n" + " Import from .reg file. Where for example is HKEY_LOCAL_MACHINE\\SOFTWARE\n" + " Only one .reg and one hive file supported at the same time\n" "-e ...\n" - " Interactive edit one or more of registry files\n" - "-v : More verbose messages\n" + " Interactive edit one or more of registry files\n\n" + "Options:\n" + "-L : Log changed filenames to /tmp/changed, also auto-saves\n" + "-C : Auto-save (commit) changed hives without asking\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\n" ); } @@ -64,53 +76,45 @@ int main(int argc, char **argv) { - int export = 0, edit = 0; + int export = 0, edit = 0, import = 0; int d = 0; - int update = 0; + int autocommit = 0, update = 0; int logchange = 0, mode = 0, dd = 0; int il; extern int optind; extern char* optarg; - char *hivename, *prefix, *key, *outputname; + char *hivename, *prefix, *key, *outputname, *inputname; char c; char yn[10]; FILE *ch; - char *options = "vhxe"; + char *options = "vhtxCLeINE"; printf("%s\n",reged_version); while((c=getopt(argc,argv,options)) > 0) { switch(c) { case 'e': edit = 1; break; case 'x': export = 1; break; + case 'I': import = 1; break; + case 'C': autocommit = 1; break; + case 'L': logchange = 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': usage(); exit(0); break; default: usage(); exit(1); break; } } - if (!export && !edit) { + if (!export && !edit && !import) { usage(); exit(1); } - if (export && edit) { - fprintf(stderr,"Edit and xport cannot be done at same time\n"); + if ( import && export ) { + fprintf(stderr,"Import and export cannot be done at same time\n"); usage(); exit(1); } - if (edit) { /* Call editor. Rest of arguments are considered hives to load */ - hivename = argv[optind+no_hives]; - do { - if (!(hive[no_hives] = openHive(hivename, - HMODE_RW|mode))) { - printf("Unable to open/read a hive, exiting..\n"); - exit(1); - } - no_hives++; - hivename = argv[optind+no_hives]; - } while (hivename && *hivename && no_hives < MAX_HIVES); - regedit_interactive(hive, no_hives); - update = 1; - } if (export) { /* Call export. Works only on one hive at a time */ hivename=argv[optind]; prefix=argv[optind+1]; @@ -134,20 +138,67 @@ no_hives++; } + + if (import) { /* Call import. Works only on one hive at a time */ + hivename=argv[optind]; + prefix=argv[optind+1]; + inputname=argv[optind+2]; + if (gverbose) { + printf("hivename: %s, prefix: %s\n",hivename,prefix); + } + + if (!hivename || !*hivename || !prefix || !*prefix || !inputname || !*inputname) { + usage(); exit(1); + } + + if (!(hive[no_hives] = openHive(hivename,HMODE_RW|mode))) { + fprintf(stderr,"Unable to open/read hive %s, exiting..\n",hivename); + exit(1); + } + + import_reg(hive[no_hives], inputname, prefix); + + no_hives++; + update = 1; + if (edit) regedit_interactive(hive, no_hives); + edit = 0; + + } + + if (edit) { /* Call editor. Rest of arguments are considered hives to load */ + hivename = argv[optind+no_hives]; + do { + if (!(hive[no_hives] = openHive(hivename, + HMODE_RW|mode))) { + printf("Unable to open/read a hive, exiting..\n"); + exit(1); + } + no_hives++; + hivename = argv[optind+no_hives]; + } while (hivename && *hivename && no_hives < MAX_HIVES); + regedit_interactive(hive, no_hives); + update = 1; + } + if (update) { /* run for functions that can have changed things */ printf("\nHives that have changed:\n # Name\n"); for (il = 0; il < no_hives; il++) { if (hive[il]->state & HMODE_DIRTY) { - if (!logchange) printf("%2d <%s>\n",il,hive[il]->filename); + if (!logchange && !autocommit) { + printf("%2d <%s>",il,hive[il]->filename); + if (hive[il]->state & HMODE_DIDEXPAND) + printf(" WARNING: File was expanded! Experimental! Use at own risk!\n"); + printf("\n"); + } d = 1; } } if (d) { /* Only prompt user if logging of changed files has not been set */ /* Thus we assume confirmations are done externally if they ask for a list of changes */ - if (!logchange) fmyinput("Commit changes to registry? (y/n) [n] : ",yn,3); - if (*yn == 'y' || logchange) { + if (!logchange && !autocommit) fmyinput("Commit changes to registry? (y/n) [n] : ",yn,3); + if (*yn == 'y' || logchange || autocommit) { if (logchange) { ch = fopen("/tmp/changed","w"); } @@ -155,7 +206,10 @@ if (hive[il]->state & HMODE_DIRTY) { printf("%2d <%s> - ",il,hive[il]->filename); if (!writeHive(hive[il])) { - printf("OK\n"); + printf("OK "); + if (hive[il]->state & HMODE_DIDEXPAND) + printf(" WARNING: File was expanded! Experimental! Use at own risk!\n"); + printf("\n"); if (logchange) fprintf(ch,"%s ",hive[il]->filename); dd = 2; } @@ -171,7 +225,9 @@ } else { printf("None!\n\n"); } - } /* list only check */ + } + while (no_hives > 0) + closeHive(hive[--no_hives]); return(dd); } Binary files /tmp/SX_uc0o7KC/chntpw-0.99.6/reged.static and /tmp/BqcNAvCQc1/chntpw-0.99.6.110511/reged.static differ diff -Nru chntpw-0.99.6/regedit.txt chntpw-0.99.6.110511/regedit.txt --- chntpw-0.99.6/regedit.txt 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/regedit.txt 2011-05-11 19:33:56.000000000 +0000 @@ -1,6 +1,6 @@ The Offline NT Password Editor -(c) 1997-2003 Petter Nordahl-Hagen +(c) 1997-2011 Petter Nordahl-Hagen Registry Editor Usermanual/docs @@ -8,29 +8,85 @@ See INSTALL for compile/installation instructions. See README for docs on the passwordpart (or website for bootdisk) -January 2003: +Some known limitations as of first half 2011: This release features full basic registry edit with -add/del keys and values and resizing values. -Limitations/not implemented yet: -- Not possible to expand the hive file with new pages. - Only existing free space within the current filesize - can be used to add keys/values. -- When there are many subkeys of a key, the indexing will be - different, and this is not supported yet when adding/removing keys. - Will give error "not 'lh' or 'lf' index.." -- NT3.51 add/del subkeys not supported yet. -- Ability to start a new fresh blank hive is on the TODO list. -- In some cases there seems to be space at the end of the hive file - but that space is not covered in the usual page structure. - This may cause some errors during registry edit: - "Ran off end of page.." or "zero size page len.." (and exit). - If the edit continues, it's usually safe to continue, - but you may lose some space in the file. - I'm investigating this. +add/del keys and values and resizing values, and also +expanding the file. +Renaming keys and values is not implemented yet. +Import is slow. Export is fast. + +Program "reged" is used for interactive registry edit +(rudimentary command prompt type interface) +or to export or import .reg files. +Import and export can also be scripted (non-interactive) + +The "chntpw" program which is for password edits, also +contains the editor, but not the import / export. + +Usage of "reged": + + reged version 0.1 110504, (c) Petter N Hagen + + Modes: + -x + Xport. Where for example is HKEY_LOCAL_MACHINE\SOFTWARE + is key to dump (recursively), . or \ or \\ means all keys in hive + Only one .reg and one hive file supported at the same time + -I + Import from .reg file. Where for example is HKEY_LOCAL_MACHINE\SOFTWARE + Only one .reg and one hive file supported at the same time + -e ... + 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 + + +-x will do export to .reg file. +It will create files that seems to be compatible with regedit.exe in +Windows. +The prefix is the first part of the key names that windows shows/uses, +it is not stored in the hive files, so you can actually put in +anything unless you have to import in Windows. +Current version only handles one set of files at a time (and also only +one point to start export from in it) + +-I does import of .reg files into the specified hive file. +It supports .reg files from Windows regedit.exe, they are usually +UTF-16 (16 bit characters) and in the few cases I tested it will +import with correct characters. Please note that the key and value +names always are 8 bit in the registry, but strings the values store +are 16 bit characters. +Will also work with latin-1 (8 bit) character files, like the export +functions creates, but again some character conversions may be wrong. +Note that the current versions are pretty slow, since they are not +optimized or well written in any way. Around 90000 keys with 120000 +values just took around 10 minutes on my pretty fast machine. +(I am lazy! You don't write hex reads with one byte at a time do you? :) + +-e goes into interactive editor (see below for old example) +-e can be combined with -I so that the editor is entered before saving +-after an import. + +-N and -E are safe modes, can be used with any mode. +-C must be used when importing to auto-save, else it will ask first if +import succeeds. +The -L option will write changed hive file names to /tmp/changed, +I use it for scripts that need to know. + + +------------------------ + + +Here is an old demo of registry edit, via the chntpw program, +but regedit is the same (except some new stuff is in there) -This is a short demo of the registry editor-part, should give you -an idea on how it works. You can navigate the registry almost like a filesystem (only difference being that the "files" actually are of a special datatype, instead of just a bytestream) @@ -312,6 +368,7 @@ [1c2ef8] \DemoKey> nv 1 test (type 1 is string, 3 binary, 4 dword) + (HINT: type "nv h" for help) [1c2ef8] \DemoKey> l ls of node at offset 0x1c2efc diff -Nru chntpw-0.99.6/sam.h chntpw-0.99.6.110511/sam.h --- chntpw-0.99.6/sam.h 2008-05-26 19:59:44.000000000 +0000 +++ chntpw-0.99.6.110511/sam.h 2011-05-11 19:33:56.000000000 +0000 @@ -4,7 +4,7 @@ ***** * * NTREG - Window registry file reader / writer library - * Copyright (c) 1997-2007 Petter Nordahl-Hagen. + * Copyright (c) 1997-2011 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 diff -Nru chntpw-0.99.6/syskey.txt chntpw-0.99.6.110511/syskey.txt --- chntpw-0.99.6/syskey.txt 1970-01-01 00:00:00.000000000 +0000 +++ chntpw-0.99.6.110511/syskey.txt 2011-05-11 19:33:56.000000000 +0000 @@ -0,0 +1,124 @@ +The Offline NT Password Editor + +(c) 1997-2002 Petter Nordahl-Hagen + +Update: 08 dec 2002 + +What happens when syskey is installed, and how to get rid of it +--------------------------------------------------------------- + +Background: +----------- + +Syskey was added to NT with Service Pack 3 as a way to prevent easy +access to the actual password hashes in the SAM (Security Accounts Manager) +The original methods of making and storing the hashes makes it +rather easy to bruteforce or dictionary-attack it to find the plaintext +passwords. (mostly caused by a somewhat flawed implementation & use +of the cryptoalgorithms involved, but that's discussed elsewhere) +Enabling syskey is optional, the administrator must run syskey.exe and +answer some dialog-boxes to turn it on. On Windows 2000 it's not optional +anymore, it's enabled by default at installation time. + +When syskey is active, the hashes are encrypted/obfuscated yet +another time before being stored in the SAM registry. +However, they're stored in the old form in memory after boot +(pwdump2 demonstrates this), +since the old form is needed for NTLM authentication on the network etc. + +The key that obfuscates the hashes, or rather it looks like something +that decrypts the key, can be stored on floppy, generated from a +passphrase to be entered at boot, or stored (obfuscated again) in +the registry. + +There's no official supported method to switch off syskey +once activated, except restoring the registry from a rescuefloppy +made before activation of syskey. + +So.. what's this got to do with my utility? +------------------------------------------- + +My utility doesn't try to crack passwords, it puts new hashes into +the SAM, thus changing a users password. And it does this offline. +Syskey was a showstopper for this. +As far as I can see, there's 2 ways to solve this: + +1) Find the key in registry, get user to enter it, or get hold of floppy + then use the syskey on the new password too. However, it's not documented + and I haven't found any reverse engineering of it anyplace. + +2) Try to turn it off. This has one drawback, and one good side: + Bad: all passwords must be reset, since the old hashes will be invalid. + VeryBAD: SWITHCHING OFF IN WINDOWS 2000 AND XP NOT PERFECT, + WILL CAUSE TROUBLE, but you can access the computer + afterwards. Domain relationships & syskey may be + impossible to change after this, requiring a reinstall + (or possibly only an upgrade) + Good: There's no need for the key (which may be lost). + +3) (NEW 2000-04-01, no, not a joke) Insert old styles password-hashes + into the SAM, will be converted to syskey-hashes on next boot. + This is how syskey is enabled on NT4, the hashes won't be touched + until the first reboot after turning on syskey. + +I've found out how to do #2 and #3. + +What happens when syskey is turned on, and how to turn it off again: +-------------------------------------------------------------------- + +- 1 - +Serveral new keys are added to HKLM\System\CurrentControlSet\Control\Lsa, +it seems that most of the keys/values is used for the obfuscation of the key +they change when syskey is updated. +However the value named 'SecureBoot' holds the mode of syskey: + 1 - Key in registry + 2 - Enter passphrase + 3 - Key on floppy + +But removing this key (or setting it to 0) isn't enough to disable +syskey. There's more.. + +- 2 - +HKLM\SAM\Domains\Account\F is a binary structure usually containing the computer +SID and some other stuff related to that. +When syskey is installed it's expanded (about twice the size), with something +I guess is the key heavily encrypted + some flags and other values. +One of these other flag/values also contains the same mode as SecureBoot above. + +So.. resetting this mode flag and SecureBoot to 0 is all that's needed +to switch off syskey in NT4 (up to SP6 at time of writing). Changing only one of them +results in a warning about inconsistencies between the SAM and system settings +on completed boot, and syskey is re-invoked. + +- 3 - +On Windows 2000 there's yet another place info about syskey is stored: + +HKLM\security\Policy\PolSecretEncryptionKey\ +which also is a binary structure, but also there the mode is stored. +Reset this to 0, and syskey is gone on win2k. +(if there's a mismatch between the three, it silently resets them + to the most likely value on boot) + +- 4 - +Then there's the password hashes. +The usual (old) hashlength is 16 bytes, but all hashes are expanded to 20 bytes +with syskey, the first 4 bytes looks like some kind of counter. (maybe +history-counter?). +Strangely, they're not updated at once when syskey is turned on, +update of the hashes happens during next reboot after syskey has been turned on. +And when the key is later updated, the hashes are also updated? +NO!! Strangely it SEEMS like the password hashes REMAINS THE SAME! +(however, the binaries in the 3 keys noted above changes..) +I'll try to dig more into this. Help wanted :) + +When syskey has been switched off, all passwords must be reset. +My utility will write and adjust hash-lengths of the users (usually +administrator) that you reset the password for. +NT itself will fix the rest of the hashes when you set new passwords +from NT. + +And yes, it's possible to re-enable syskey after turning it off. +(not on win2k, yet!) + +So, anybody reverse engineered the whole syskeystuff? +(yes, I know something's on it's way..)