From 05445b7200e0229b6d00f9e023f5858f570cb90f Mon Sep 17 00:00:00 2001 From: Adrian Gibanel Lopez Date: Sun, 11 Aug 2013 00:02:31 +0200 Subject: [PATCH] Initial commit of 11 May 2011 chntpw source code --- COPYING.txt | 340 ++++ GPL.txt | 340 ++++ HISTORY.txt | 316 ++++ INSTALL.txt | 48 + LGPL.txt | 504 ++++++ Makefile | 55 + README.txt | 249 +++ WinReg.txt | 663 ++++++++ chntpw-before.c | 1780 ++++++++++++++++++++ chntpw.c | 1301 +++++++++++++++ cpnt.c | 100 ++ edlib.c | 691 ++++++++ hash.c | 66 + ntreg.c | 4239 +++++++++++++++++++++++++++++++++++++++++++++++ ntreg.h | 444 +++++ reged.c | 233 +++ regedit.txt | 438 +++++ sam.h | 222 +++ syskey.txt | 124 ++ 19 files changed, 12153 insertions(+) create mode 100644 COPYING.txt create mode 100644 GPL.txt create mode 100644 HISTORY.txt create mode 100644 INSTALL.txt create mode 100644 LGPL.txt create mode 100644 Makefile create mode 100644 README.txt create mode 100644 WinReg.txt create mode 100644 chntpw-before.c create mode 100644 chntpw.c create mode 100644 cpnt.c create mode 100644 edlib.c create mode 100644 hash.c create mode 100644 ntreg.c create mode 100644 ntreg.h create mode 100644 reged.c create mode 100644 regedit.txt create mode 100644 sam.h create mode 100644 syskey.txt diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING.txt @@ -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 --git a/GPL.txt b/GPL.txt new file mode 100644 index 0000000..7a8e8ab --- /dev/null +++ b/GPL.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, 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 Street, 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 Lesser General +Public License instead of this License. diff --git a/HISTORY.txt b/HISTORY.txt new file mode 100644 index 0000000..39a50c9 --- /dev/null +++ b/HISTORY.txt @@ -0,0 +1,316 @@ + +The Offline NT Password Editor + +(c) 1997-2011 Petter Nordahl-Hagen + +See README for general info, copyright & credits. +See INSTALL for compile/installation instructions. + +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 + (still also available in chntpw) plus + the registry export to .reg-file from command line. + This tool contains no password functions, thus no crypto. + chntpw: + - Interactive reg ed moved out of chntpw, into edlib.c + - 64 bit compatible patch by Mike Doty, via Alon Bar-Lev + http://bugs.gentoo.org/show_bug.cgi?id=185411 + - Some minor tweaks and fixes + ntreg library: + - Type QWORD (XP/Vista and newer) now recognized + - Most functions accepting a path now also have a parameter specifying if + the search should be exact or on first match basis, thus + this libary version breaks build with older programs. + - Fixed bug which skipped first indirect index table when deleting keys, + usually leading to endless loop when recursive deleting. + - Export to .reg file by Leo von Klenze, expanded a bit by me. + - 64 bit compatible patch by Mike Doty, via Alon Bar-Lev + http://bugs.gentoo.org/show_bug.cgi?id=185411 + - A few other small fixes and tweaks. + +* v 0.99.5 070923 + - User edit is now menu-driven, better visuals / tables / listings. + - Group memberships are listen on the user + - User promotion (put into admin group) is now public, with it's own + menu selection in the user menu. Still considered EXPERIMENTAL! + - Some other verbosity/visual changes + - Changed the verbosity (-v) option to be more useful for debug + or advanced information + +* v 0.99.4 070406 (decade) + - 10 year anniversary! + - Changed to GPL license + - Support for Windows Vista was already there, but only blanking + password works. + - Some small insignificant changes, mainly to output or text. + +* v 0.99.3 041205 + - Fixed bug in loadhive(), caused a crash when last page pointer + was out of range, which seems to happen a lot on some + installations. Guess it is correct to just assume it is + end of file. Thanks to Michael Rothstein for supplying a + hive with this. + +* v 0.99.3 040818 + chntpw: + - Lot of nasty bugs fixed in REG_SZ and REG_MULTI_SZ input, often + trashed the heap, leading to later crashes. + - New regedit command "hex" to see value in hexdump regardles + of type. + - Some other minor fixes on hive load. + ntreg: Major bugfixes in key add/delete, + and support for more of the registry: + - Now full support of key add/del on NT351 ('li' index) + as a bi-effect this also adds support for NT4/win2k edits + in keys with indirect index (when it contains ~>500 subkeys) + since the index tables then switches from 'lf' (semi-hashed) + to 'li' (unhashed table). + - WinXP and newer uses 'lh' (true hash) always, even when going + via indirect tables ('ri'). I wasn't aware of this, so reading + or traversing that in earlier versions usually crashed. + - Critical bugs fixed in the allocation of space in the hive, + older versions may in several occations corrupt the hive. + - Fixed some hive load bugs, deallocate properly on close etc. + +* v 0.99.2 040105 + - Added RecoveryConsole parameter change + - Try to guess type of hive loaded (sam, system, security etc), this + is used by the user edit, syskey edit, RecoveryConsole edit + to look up into the right hive. + - Do not confirm save if the change-tracking option is set (-L) + (assume wrapper does it..) + - Hid the hash-stuff during password reset. I got all kinds of + confusing questions about it (most often: can I find the original + pw? Answer: NO if win2k or higher) + Option -v turns this on again for debugging. + - Also reduced some other debug output, -t and/or -v turns on again + most of it. + - Fixed some bugs: + - check of return value from open() + - corrected prototype defs for some calls into OpenSSL, + this fixes some compilation errors people have reported. + Thanks to Alexander Poquet for that. + - Probably _added_ more bugs :-} but they are yet unknown :) + +* v 0.99.1 030126 + - Fixed bug in ntreg, seems some files has garbage instead of zero + pages at end. Now stops enumerating on first non 'hbin' page. + Thanks to Jim Andersen for giving me a hive file that showed this. + - Reports strongly indicates that blanking a password + (* at the prompt) works more often than changing passwords. + Changed some wording regarding this. + +* v 0.99.0 030112 + - There is now full registry edit support with expansion of value + size and add / delete of keys and values! + See regedit.txt for more info on registry editing + See source for details on data block allocation etc in registry. + - Changed interactive mode (-i) a bit, now features menues. + - Syskeystatus/change not shown unless selected from menu. + - New option: -N to set it in old-edit mode, ie only exact + overwrites. This is for safety in testing etc. + +* v 0.98.6 021213 + - Added parsing of users F and SAMs F value, which stores account type bits + and lockout information. Ability to reset it if locked. + - As usual, see source (especially sam.h for details) on this. + +* v 0.98.5 021208 + - Added ability to enter RID (in hex) instead of username by + starting the entry with 0x. Works interactive and on commandline. + This will enable edit of users that has names which actually + use unicode, like some russian and asian systems. +- Only entering * for password will blank password by setting + hash lengths to zero. Seems to be working ok in 2k and XP. + This may also clear up an earlier failed change. + One bug/drawback: Seems to leak about 30-50 bytes in the V struct. +- Fixed bug in handling of blank LANMAN-pass. + +* v 0.98.4 011022 + - Fixed input buffer overflow that made it impossible to enter + usernames at 16 chars (the \n would flow over into the password + prompt thus not changing it) + - New option -L that will write the names of changed hives to + /tmp/changed. Mainly for use in my bootfloppy scripts. + +* v 0.98.3 010808 + - Blank password detection rewritten. The NT or LANMAN hash seems + to actually be missing from the sam entry if password is blank. + This will now be detected, and if only LANMAN is present, it + will be convertet to NT password. + - Some minor changes in text & prompts. + +* v 0.98.2 010107 + - Changed to use OpenSSL for MD4 & DES encryption instead of + the old libraries. Only change of build process from source + is to point the Makefile to wherever your OpenSSL libraries & includes are. + - Minor changes in wording of Syskey warnings. + - No functional changes. + - Known bugs: Hive statistics often wrong (but the numbers are not used + later, so it's not critical) + +* v 0.98.1 000607 + + - Discovered that when a key has lots of subkeys (typically about + 300 or more?) its hashtable is split and accessed through an + indirect ('ri') table, almost as some filesystems do with + datablock lists on large files. This caused an error "Not 'nk' node" + and sometimes a crash in earlier versions. In SAM this happens in + \SAM\Domains\Account\Users when the number of users are large + (which often is the case on domain controllers). + See source code for technical details on this. (WinReg.txt not updated) + +* v 0.98 000401 + + - Disabling syskey on Win2000 corrupts the SAM somehow, makes it + unable to reenable + leaves it in a partial mode 1 syskey. + Also corrupts the Domain Trust password, making it impossible + to join og leave a domain. + Delete HKLM\SECURITY\Policy\Secrets\$MACHINE.ACC (the trust key, + may need to change ACLs to get to it) to leave the domain and make it + possible to rejoin. + - Added support for changing passwords without disabling or + changing syskey, simply by inserting old type password hash + into the sam. It gets converted to syskey-hash on next boot. + Why I didn't discover this before is beyond me. + - A few smaller bugfixes. + +* v 0.98 000215 + + - Can now write, too, but only existing values, & same datalength. + - Internals rewritten, registry manipulation functions + now in separate "library". + - Added ability to check for and disable syskey. (see syskey.txt + & sourcecode for details) + - "Command line"-type registry-editor improved a bit. + - Possible to load and edit several hives at once + - Several minor changes. + - Since a lot has been rewritten, there's probably more bugs. + +* v 0.90 990307 + +Major updates: + + - Full registry parse/read/viever. Can follow the tree like a filesystem. + No write-support yet. + - 3.51 & 4.0 support. Seems to be working on NT5 betas, too. + - 100% sure to find usernames/passwords. + - Support for localized admin-names (will automatically find it) + - Several minor bugs from earlier versions fixed. + - Sorry, but still no syskey support. (but since full registry + read capabilities exsist, it's now easier to make it) + +* PRE-1 970611 + - Got reports on "Netlogon service not started" + (and then it was impossible to logon) + if attempt to change blank password into something else. + This was because if the password is set blank (from NT), the NT-logon + hash (md4) is not stored in SAM at all (the LANmanager hash seems to be + there..) However, chntpw didn't know about this, and happily(?) wrote away + and corrupted the SAM file, causing Netlogon to fail. + This version will flag accounts with blank passwords, and refuse to change it. + + - The -i (interactive) option now loops. Will ask for a new name repeatedly, + until '!' is entered. This enables you to change multiple passwords in + one run. + + - No support for syskey, yet. + +* PRE-1 970528: + - First public release + diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 0000000..d2639c9 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,48 @@ + +The Offline NT Password & Registry Editor + +(c) 1997-2011 Petter Nordahl-Hagen + +This file is meant for developers. + +General usage is via the provided boot CD images, but +static linux builds of the tools are also available +in the source zip file. + +See REAMDE for general instructions and license info, +HISTORY.txt for version info. + +If you want to build yourself, it should compile +with most gcc compilers. However, I am not particulary good +with types, so there may be some warnings. + +You may have to install the openssl-devel package from you +linux distro to compile/link, since I need the static variant. +(change the makefile to link dynamic if you wish) +I link statically with libcrypto.a from OpenSSL. +(chntpw only, other tools does not require crypto) + +Thanks to Denis Ducamp for modifying chntpw to use OpenSSL. + +Thanks to Mike Doty, via Alon Bar-Lev for 64 bit build compatible patch. + (from http://bugs.gentoo.org/show_bug.cgi?id=185411) + +Look into Makefile, you may need to change things to fit your env. +(also if you need 64 bit) + +If make goes well, you should have: + chntpw - Password tool, dynamic with libc, static with OpenSSL + chntpw.static - Completely static passord tool + 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 + 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 +for details. (build scripts not currently available..) + +So, note that the binary then contains cryptographic code +which may be illegal for you to export. + +Good luck. diff --git a/LGPL.txt b/LGPL.txt new file mode 100644 index 0000000..a2a1d92 --- /dev/null +++ b/LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e267b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# +# Makefile for the Offline NT Password Editor +# +# +# Change here to point to the needed OpenSSL libraries & .h files +# See INSTALL for more info. +# + +#SSLPATH=/usr/local/ssl +OSSLPATH=/usr +OSSLINC=$(OSSLPATH)/include + +CC=gcc + +# Force 32 bit +CFLAGS= -DUSEOPENSSL -g -I. -I$(OSSLINC) -Wall -m32 +OSSLLIB=$(OSSLPATH)/lib + +# 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 + +all: chntpw chntpw.static cpnt reged reged.static + +chntpw: chntpw.o ntreg.o edlib.o + $(CC) $(CFLAGS) -o chntpw chntpw.o ntreg.o edlib.o $(LIBS) + +chntpw.static: chntpw.o ntreg.o edlib.o + $(CC) -static $(CFLAGS) -o chntpw.static chntpw.o ntreg.o edlib.o $(LIBS) + +cpnt: cpnt.o + $(CC) $(CFLAGS) -o cpnt cpnt.o $(LIBS) + +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) + +# -Wl,-t + +.c.o: + $(CC) -c $(CFLAGS) $< + +clean: + rm -f *.o chntpw chntpw.static cpnt reged reged.static *~ + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..126b8de --- /dev/null +++ b/README.txt @@ -0,0 +1,249 @@ + +The Offline NT Password Editor + +(c) 1997-2011 Petter Nordahl-Hagen + +This is free software, licensed under the following: + +"ntreg" (the registry library) is licensed under the GNU Lesser Public +License. See LGPL.txt. + +"chntpw" (the password reset / registry editor frontend) is licensed +under the GNU General Public License, see GPL.txt. + +"reged" (registry editor /export tool) is licensed +under the GNU General Public License, see GPL.txt. + +See INSTALL.txt for compile/installation instructions. + +Where to get more info: +----------------------- + +http://pogostick.net/~pnh/ntpasswd/ + +At that site there's a floppy and a bootable CD that use chntpw to +access the NT/2k/XP/Vista-system it is booted on to edit password etc. +The instructions below are for the standalone program itself, not the floppy. + +What does chntpw do? +-------------------- + +This little program will enable you to view some information and +change user passwords in a Windows (NT/XP/Vista/win7) etc SAM userdatabase file. +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? +---- + +I often forget passwords. Especially on test installations (that +I just _must_ have some stuff out of half a year later..) +On most unix-based boxes you just boot the thingy off some kind +of rescue bootmedia (cd/floppy etc), and simply edit the +password file. +On Windows NT however, as far as I know, there is no way except reinstalling +the userdatabase, losing all users except admin. +(ok, some companies let you pay lotsa $$$$$ for some rescue service..) + +How? +---- + +Currently, this thing only runs under linux, but it may just happen +to compile on other platforms, too. +(there are dos-versions available, look for links on my webpage) +So, to set a new adminpassword on your NT installation you either: +1) Take the harddrive and mount it on a linux-box +2) Use a linux-bootdisk or CD + one is available at: http://pogostick.net/~pnh/ntpasswd/ +ie. you do it offline, with the NT system down. + +Usage: +------ + +This is usage of the "chntpw" program binary only. +For info on the bootdisk, see the web site. +Some of the output format has changed a little since the docs were +first written. + + chntpw version 0.99.2 040105, (c) Petter N Hagen + chntpw: change password of a user in a NT SAM file, or invoke registry editor. + chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...] + -h This message + -u Username to change, Administrator is default + -l list all users in SAM file + -i Interactive. List users (as -l) then ask for username to change + -e Registry editor. Now with full write support! + -d Enter buffer debugger instead (hex editor), + -t Trace. Show hexdump of structs/segments. (deprecated debug function) + -v Be a little more verbose (for debuging) + -L Write names of changed files to /tmp/changed + -N No allocation mode. Only (old style) same length overwrites possible + +Normal usage is: + +> chntpw sam system security + - open registry hives 'sam' and 'system' and change administrator account. + Verions dated later from Feb 1999 and later also supports + and will find the admin account, even if the name has been changed, + or the name has been localized (different languageversion of NT + use different admin-names) + +The -u option: +Specifies user to change: + +> chntpw -u jabbathehutt mysam + - Prompt for password for 'jabbathehutt', if found (otherwise do nothing) + +Or you may give RID number in hex: +> chntpw -u 0x1f4 mysam + - Will edit administrator. + +Names does not support multibyte (unicode) characters like +some russian and asian locales. Give RID in hex to edit users +with such names. Must start with 0x. Ex: 0x2fa + +The -l option: + Will list all users in the sam-file. + +The -i option: + Go into the interactive menu system. + +The -d option: + This will load the file, and then immediately enter the + buffer debugger. + This is a simple hex-editor with only a few commands, + enter ? at the . prompt to se a short command overview. + 'q' exits without saving, 's' exit and saves. + +The -e option: + Will enter the registry editor. + You can navigate the registry like a filesystem at the command-line prompt: + See regedit.txt file for more info. + +The -t option: + This is a debug function (extended -l) to show how it traces the chain + of structs in the file. This also includes a raw interpretation + of the different registry structures + a hex dump. + +The -L option: + Drops the filenames of the changed hives in /tmp/changed + Used by the bootdisk scripts. + +The -N option: + Will fall back to old edit mode, disable the block allocations + and only support overwrite-same-size. Used to ensure safety + in testing period. + +How does it work: +----------------- + +A struct, called the V value of a key in the NT registry +was suddenly somewhat documented through the pwdump utility +included in the unix Samba distribution. +This struct contains some info on a user of the NT machine, +along with 2 crypted versions of the password associated +with the account. + +One password is the NT console login password, +the other the LANMAN network share password +(which essentially is the first one in uppercase only, + and no unicode) + +This is how NT encrypts the passwords: + +The logon cleartext password a user enters is: +1) Converted to unicode +2) A MD4 hash is made out of the unicode string +3) Then the hash is crypted with DES, using the RID (lower + part of the SID, userid) as the crypt key. + This is the so called "obfuscation" step, so + it's not obvious on a hex dump of the file + that two or more users have the same password. +4) The result of stage 3 (16 bytes) is put into the V struct. + +For the LANMAN password: +1) Uppercased (and illegal characters probably removed) + 14 bytes max, if less the remaining bytes are zeroed. +2) A known (constant) string is DES-encrypted + using 7 first characters of the password as the key. + Another constant is encrypted using the last 7 chars + as the key. + The result of these two crypts are simply appended, + resulting in a 16 byte string. +3) The same obfuscation DES stage as 3 above. +4) 16 bytes result put into the V struct. + +Since the number of possible combinations in the lanman +password is relatively low compared to the other one, +and it's easy to see if it's shorter than 8 chars or not +it's used first in brute-force-crackers. + +This program, however, don't care at all what the old +one is, it just overwrites it with the new one. + +Ok. So, how do we find and identify the V struct? +Yeah.. that was the hard part.. The files structure +is not documented (as far as I know..) + +But, with help from an unnamed German, and a lot of testing +and guesswork from myself, it's now possible to follow +the actual registry tree. (see source code for struct-defines +and comments on the registry structure) + +The usernames are listed in: +\SAM\Domains\Account\Users\Names\ + +[2d18] \SAM\Domains\Account\Users\Names> l +ls of node at offset 0x2d1c +Node has 4 subkeys and 1 values +nk-offset name +0x003290 - +0x003630 - +0x001c88 - +0x003428 - + +Each name is a subkey, with one namless value containing +the RID. + +[2d18] \SAM\Domains\Account\Users\Names> cd pnh + +[3428] \SAM\Domains\Account\Users\Names\pnh> l +ls of node at offset 0x342c +Node has 0 subkeys and 1 values +vk-offs size type name +0x003688 0 (unknown) <> INLINE: val (in type field?): 1000 (0x3e8) + +To get the userinfo (V struct), access +\SAM\Domains\Account\Users\\V + +[2c90] \SAM\Domains\Account\Users> l +ls of node at offset 0x2c94 +Node has 5 subkeys and 1 values +nk-offset name +0x003320 - <000001F4> +0x0036b8 - <000001F5> +0x003550 - <000003E8> +0x001d00 - <000003E9> +0x002d18 - + +[2c90] \SAM\Domains\Account\Users> cd 000003E8 + +[3550] \SAM\Domains\Account\Users\000003E8> l +ls of node at offset 0x3554 +Node has 0 subkeys and 2 values +vk-offs size type name +0x0035a8 80 REG_BINARY +0x003228 508 REG_BINARY + +For more techincal info, look it up in the source code. diff --git a/WinReg.txt b/WinReg.txt new file mode 100644 index 0000000..7c26930 --- /dev/null +++ b/WinReg.txt @@ -0,0 +1,663 @@ +Agreement: +========== + +The author of this document will not be responsible for any damage and/or +license violation that may occur. The information within this document is +provided "as is" without warranty of any kind... +This information was "collected" during sleepless nights, and is NOT +officially released by Microsoft! It shall give you a peek at the Windows(tm) +internals to give you a chance to recover from corrupted data. + +The author has nothing to do with Microsoft, except that he uses their +products... + +If you don't agree with this, stop reading this document, and delete it at +once! + + +History: +======== + +What is the registry? Where did it came from? Two questions, which I will try to +answer here. The registry is a database (at least microsoft thinks so:) +which contains configuration information about the system. +It mainly is a memory dump which is saved to one or more files on the windows +host drive. It is loaded every system-boot and remains resident until +shutdown. Since parts of it are not used during normal operation it will be +swapped out very soon. The registry appeared with windows 3.?? (sorry, I can't +remember any earlier version :-), where it was used for file associations and +the "OLE" functions (the conection between ole-id's and the applications). +This is a critical information and since the registry has (almost) NO +CHECKSUM information (!), it sometimes gets corrupted. This is the main +reason for this doc. + +Using windows 3.x, almost every configuration was done using good old ".INI"- +files, which were readable but slow and limited in size (64k). In windows 95 +(and NT), the registry was used instead of these files. So, to edit a +particular setting, you would have to run the application which manages these +settings. :( but what if this app won't start? MS included a tool named +REGEDIT in windows 3.?? and 95, and a REGEDT32 in windows NT. You can use +these apps to edit ALL contents of the registry (in windows NT the registry +supports security, as well as it provides the security for the whole system!) + +An application can open a "key", write values (variables) to it and fill them +with data. Each key represents also a value called "default" and can contain +any number of sub-keys. This will form a tree-structure as you can see at +the left half of REGEDIT. (note: REGEDIT from windows 3.?? has to be started +with /V or /Y, I can't remember now) + + +Where can I find the registry??? +================================ + +That differs for each windows-version: + +Version File(s) Contents +3.1x REG.DAT Complete windows 3.?? Registry + +95 SYSTEM.DAT System-values (HKEY_LOCAL_MACHINE) + USER.DAT User-values (HKEY_USERS) + +NT SYSTEM32\CONFIG\SAM SAM-part of the registry (=NT Security) + SYSTEM32\CONFIG\SOFTWARE Software-Specific part + (HKEY_LOCAL_MACHINE\SOFTWARE) + SYSTEM32\CONFIG\SYSTEM System-specific part + (HKEY_LOCAL_MACHINE\System) + PROFILES\%USERNAME%\NTUSER.DAT User-Specific part + (HKEY_CURRENT_USER\{S-1-xxx...}) + PROFILES\%USERNAME%\NTUSER.MAN like NTUSER.DAT but a + MANDATORY-profile + +If you are using a ROAMING-profile with windows NT, NTUSER.xxx can be on +a network-share as well... + + + +Terms +===== + +The registry consists of the following elements: + + Hive: strating point of the structure. The name of an hive starts + with the "HKEY_"-prefix. Can be seen as a "drive" in a file + system. + +Hive name Beschreibung 3.1 95 NT4 +HKEY_CLASSES_ROOT Points to the "class" key in + the "HKEY_LOCAL_MACHINE" hive, + the only hive in windows 3.?? X X X + +HKEY_CURRENT_USER Information and settings valid + for the currently logged in + user. (Points to the correct X X + key under "HKEY_USERS") + +HKEY_CURRENT_CONFIG Settings for the currently + active hardware profile. + Points to "HKEY_LOCAL_MACHINE\ X X + CONTROL\CONTROLSETxxx + +HKEY_USERS Contains all currently active + user settings. Since NT is a + single user system, there + will be only one key (the S-ID X X + of the active user), and a + ".DEFUALT" key (The settings + for the CTRL-ALT-DEL environment) + +HKEY_LOCALMACHINE All local settings X X + +HKEY_DYN_DATA As the name says, here you'll find X + dynamic data (CPU-usage,...) + + + Key: A key to the registry can be seen as a directory in a file + system. + Value: can be seen as the registrys "file" + Data: is the actual setting, can be seen as the contents of a + file + + +Windows 3.x +=========== + +This registry is the easiest one. It consists of 3 blocks, which are not +"signed" at all: + +Block Position Size +Header 0 32 Bytes +Navigation-Info 0x00000020 ??? +Data-Block ??? ??? + +The "???" marked values can be read from the header. + +Header +====== + +Offset Size Description +0x0000 8 Byte ASCII-Text: "SHCC3.10" +0x0008 D-Word ? +0x000C D-Word ? (always equal the D-Word at 0x0008) +0x0010 D-Word Number of entrys in the navigation-block +0x0014 D-Word Offset of the data-block +0x0018 D-Word Size of the data-block +0x001C Word ? +0x001E Word ? + +Values marked "?" are not important for a read-access, and therefore unknown +to me... + +Navigation-Block +================ + +This is where chaos rules! It consists of two different, 8 byte long blocks: + + * Navigation-Info-Record, + * Text-Info-Record + +The first record in the navigation block is a navigation info record. + +Navigation-Info-Record + +Offset Size Contents +0x00 Word Next Key (same level) +0x02 Word First Sub-Key (one level deeper) +0x04 Word Text-Info-Record Key-Namens +0x06 Word Text-Info-Record Key-Value (default) + +The values are the locical number of the block inside the file: + + offset=blocksize*blocknumber+headersize + +since 2 of this values are constant: + + offset=8*blocknumber+0x20 + + +Text-Info-Record +================ + + +Offset Size Contents +0x00 Word ? +0x02 Word number of references to this text +0x04 Word Text-length +0x06 Word Offset of the text-string inside the data-block + +To get the text-offset inside the file you have to add this offset to the +data-offset inside the header. + +Data-Block +========== + +The data-block only consists of a collection of text-strings. Right in front +of every text is a word which may or may not have a meaning. The offset in +the text-info record points directly to the text, the text-size has to be +defined in the text-info record too. + + +Windows 95 +========== + +the Windows95-Registry Files: + +inside the windows-directory (default: C:\WINDOWS) are 2 files which are +loaded to form the registry: + + SYSTEM.DAT + +and + + USER.DAT + +This files are mapped to the following hives: + + HKEY_LOCAL_MACHINE in SYSTEM.DAT + +and + + HKEY_USERS in USER.DAT + + + +The file structure: +=================== + + +Both files have the same structure. Each of them consists of 3 blocks where +1 of these blocks can be repeated. +Every block has a 4 byte long signature to help identify its contents. + +ID Block-contents Max. size +CREG Header 32 Bytes @ Offset 0 +RGKN Directory information + (Tree-structure) ??? @ Offset 32 +RGDB The real data + (Values and data) max. 65535 Bytes an Offset ?? + +these blocks are "sticked together" with no space between them, but always +a multiple of 16 in size. + +the CREG-Block +============== + +Offset Size Inhalt +0x00000000 D-Word ASCII-"CREG" = 0x47455243 +0x00000008 D-Word Offset of 1st RGDB-block +0x00000010 D-Word # of RGDB-blocks + +all other values are not needed to read the registry... + + +the RGKN-Block +============== + +I assume that RGKN stands for ReGistry-Key-Navigation. This block contains +the information needed to built the tree-structure of the registry. This +block will be larger then 65536 bytes (0xFFFF)! + +All offset-values are RELATIVE to the RGKN-block! + +Offset Size Contents +0x00000000 D-Word ASCII-"RGKN" = 0x4E4B4752 +0x00000004 D-Word Size of the RGKN-block in bytes +0x00000008 D-Word Rel. Offset of the root-record +0x00000020 ???? Tree-Records (often the 1st Record) + +the Tree-Record +=============== + +The tree-record is a "complete" registry-key. It contains the "hash"-info +for the real data stored in this key. + +Offset Size Contents +0x0000 D-Word Always 0 +0x0004 D-Word Hash of the key-name +0x0008 D-Word Always -1 (0xFFFFFFFF) +0x000C D-Word Offset of the owner (parent)-records +0x0010 D-Word Offset of the 1st sub-sey record +0x0014 D-Word Offset of the next record in this level +0x0018 D-Word ID-number of the real key + +the 1st entry in a "usual" registry file is a nul-entry with subkeys: the +hive itself. It looks the same like other keys. Even the ID-number can +be any value. + +The "hash"-value is a value representing the key's name. Windows will not +search for the name, but for a matching hash-value. if it finds one, it +will compare the actual string info, otherwise continue with the next key. + +End of list-pointers are filled with -1 (0xFFFFFFFF) + + +The ID-field has the following format: + + Bits 31..16: Number of the corresponding RGDB-blocks + Bits 15..0: continuous number inside this RGDB-block. + + + +The hash-method: +================ + +you are looking for the key: Software\Microsoft + +first you take the first part of the string and convert it to upper case + + SOFTWARE + +The "\" is used as a seperator only and has no meaning here. +Next you initialize a D-Word with 0 and add all ASCII-values of the string +which are smaller than 0x80 (128) to this D-Word. + + SOFTWARE = 0x0000026B + +Now you can start looking for this hash-value in the tree-record. +If you want to modify key names, also modify the hash-values, since they +cannot be found again (although they would be displayed in REGEDIT) + +the RGDB-Block +============== + +Header: + +Offset Size Contents +0x0000 D-Word ASCII-"RGDB" = 0x42444752 +0x0004 D-Word Size of this RGDB-block +0x0020 ???? RGDB Records + + +RGDB-Record (Key-Information) +============================= + +Offset Size Contents +0x0000 D-Word record length in bytes +0x0004 D-Word ID-number +0x0008 D-Word ??? Size ??? +0x000C Word text length of key name +0x000E Word Number of values inside this key +0x0010 D-Word always 0 +0x0014 ???? Key-name +0x???? ???? Values + +The first size (record length) can be used to find the next record. +The second size value is only correct if the key has at least one value, +otherwise it is a little lower. + +The key-name is not 0-terminated, its length is defined by the key- +text length field. The values are stored as records. + + +Value-Record +============ + +Offset Size Contents +0x0000 D-Word Type of data +0x0004 D-Word always 0 +0x0008 Word length of value-name +0x000A Word length of value-data +0x000C ???? value-name +0x???? ???? data + +Data-Types +========== + +value Contents +0x00000001 RegSZ - 0-terminated string (sometimes without the 0!) +0x00000003 RegBin - binary value (a simple data-block) +0x00000004 RegDWord - D-Word (always 4 bytes in size) + + + +Windows NT (Version 4.0) +======================== + +Whoever thought that the registry of windows 95 and windows nt are similar +will be surprised! They only look much the same, but have completely other +structures! +Since the RGDB-blocks in the windows 95 registry are not larger than +0xFFFF, we can see that it is optimized for a 16-bit OS... +Windows NT stores its registry in a page-oriented format with blocks +of 4kb (4096 = 0x1000 bytes) + +The windows NT registry has 2 different blocks, where one can occure many +times... + +the "regf"-Block +================ + +"regf" is obviosly the abbreviation for "Registry file". "regf" is the +signature of the header-block which is always 4kb in size, although only +the first 64 bytes seem to be used and a checksum is calculated over +the first 0x200 bytes only! + +Offset Size Contents +0x00000000 D-Word ID: ASCII-"regf" = 0x66676572 +0x00000004 D-Word ???? +0x00000008 D-Word ???? Always the same value as at 0x00000004 +0x0000000C Q-Word last modify date in WinNT date-format +0x00000014 D-Word 1 +0x00000018 D-Word 3 +0x0000001C D-Word 0 +0x00000020 D-Word 1 +0x00000024 D-Word Offset of 1st key record +0x00000028 D-Word Size of the data-blocks (Filesize-4kb) +0x0000002C D-Word 1 +0x000001FC D-Word Sum of all D-Words from 0x00000000 to 0x000001FB + +I have analyzed more registry files (from multiple machines running +NT 4.0 german version) and could not find an explanation for the values +marked with ???? the rest of the first 4kb page is not important... + + +the "hbin"-Block +================ + +I don't know what "hbin" stands for, but this block is always a multiple +of 4kb in size. + +Inside these hbin-blocks the different records are placed. The memory- +management looks like a C-compiler heap management to me... + + +hbin-Header +=========== + +Offset Size Contents +0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268 +0x0004 D-Word Offset from the 1st hbin-Block +0x0008 D-Word Offset to the next hbin-Block +0x001C D-Word Block-size + +The values in 0x0008 and 0x001C should be the same, so I don't know +if they are correct or swapped... + +From offset 0x0020 inside a hbin-block data is stored with the following +format: + + +Offset Size Contents +0x0000 D-Word Data-block size +0x0004 ???? Data + +If the size field is negative (bit 31 set), the corresponding block +is free and has a size of -blocksize! +The data is stored as one record per block. Block size is a multiple +of 4 and the last block reaches the next hbin-block, leaving no room. + + +Records in the hbin-blocks +========================== + + +nk-Record + + The nk-record can be treated as a kombination of tree-record and + key-record of the win 95 registry. + +lf-Record + + The lf-record is the counterpart to the RGKN-record (the hash-function) + +vk-Record + + The vk-record consists information to a single value. + +sk-Record + + sk (? Security Key ?) is the ACL of the registry. + +Value-Lists + + The value-lists contain information about which values are inside a + sub-key and don't have a header. + +Datas + + The datas of the registry are (like the value-list) stored without a + header. + + +All offset-values are relative to the first hbin-block and point to the block- +size field of the record-entry. to get the file offset, you have to add +the header size (4kb) and the size field (4 bytes)... + +the nk-Record +============= + +Offset Size Contents +0x0000 Word ID: ASCII-"nk" = 0x6B6E +0x0002 Word for the root-key: 0x2C, otherwise 0x20 +0x0004 Q-Word write-date/time in windows nt notation +0x0010 D-Word Offset of Owner/Parent key +0x0014 D-Word number of sub-Keys +0x001C D-Word Offset of the sub-key lf-Records +0x0024 D-Word number of values +0x0028 D-Word Offset of the Value-List +0x002C D-Word Offset of the sk-Record +0x0030 D-Word Offset of the Class-Name +0x0044 D-Word Unused (data-trash) +0x0048 Word name-length +0x004A Word class-name length +0x004C ???? key-name + +the Value-List +============== + +Offset Size Contents +0x0000 D-Word Offset 1st Value +0x0004 D-Word Offset 2nd Value +0x???? D-Word Offset nth Value + +To determine the number of values, you have to look at the +owner-nk-record! + +Der vk-Record +============= + +Offset Size Contents +0x0000 Word ID: ASCII-"vk" = 0x6B76 +0x0002 Word name length +0x0004 D-Word length of the data +0x0008 D-Word Offset of Data +0x000C D-Word Type of value +0x0010 Word Flag +0x0012 Word Unused (data-trash) +0x0014 ???? Name + +If bit 0 of the flag-word is set, a name is present, otherwise the +value has no name (=default) +If the data-size is lower 5, the data-offset value is used to store +the data itself! + + +The data-types +============== + +Wert Beteutung +0x0001 RegSZ: character string (in UNICODE!) +0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!) +0x0003 RegBin: raw-binary value +0x0004 RegDWord: Dword +0x0007 RegMultiSZ: multiple strings, seperated with 0 + (UNICODE!) + +The "lf"-record +=============== + +Offset Size Contents +0x0000 Word ID: ASCII-"lf" = 0x666C +0x0002 Word number of keys +0x0004 ???? Hash-Records + +Hash-Record +=========== + +Offset Size Contents +0x0000 D-Word Offset of corresponding "nk"-Record +0x0004 D-Word ASCII: the first 4 characters of the key-name, + padded with 0's. Case sensitiv! + +Keep in mind, that the value at 0x0004 is used for checking the +data-consistency! If you change the key-name you have to change the +hash-value too! + +The "sk"-block +============== + +(due to the complexity of the SAM-info, not clear jet) + +Offset Size Contents +0x0000 Word ID: ASCII-"sk" = 0x6B73 +0x0002 Word Unused +0x0004 D-Word Offset of previous "sk"-Record +0x0008 D-Word Offset of next "sk"-Record +0x000C D-Word usage-counter +0x0010 D-Word Size of "sk"-record in bytes +???? +???? ???? Security and auditing settings... +???? + +The usage counter counts the number of references to this +"sk"-record. You can use one "sk"-record for the entire registry! + + +Windows nt date/time format +=========================== + +The time-format is a 64-bit integer which is incremented every +0,0000001 seconds by 1 (I don't know how accurate it realy is!) +It starts with 0 at the 1st of january 1601 0:00! All values are +stored in GMT time! The time-zone is important to get the real +time! + + + +Common values for win95 and win-nt +================================== + +Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF). +If a value has no name (length=0, flag(bit 0)=0), it is treated as the +"Default" entry... +If a value has no data (length=0), it is displayed as empty. + + + +simplyfied win-3.?? registry: +============================= + + + ++-----------+ +| next rec. |---+ +-----> +------------+ +| first sub | | | | Usage cnt. | +| name | | +--> +------------+ | | length | +| value | | | | next rec. | | | text |-------> +-------+ ++-----------+ | | | name rec. |--+ +------------+ | xxxxx | + +------------+ | | value rec. |--------> +------------+ +-------+ + v | +------------+ | Usage cnt. | ++-----------+ | | length | +| next rec. | | | text |-------> +-------+ +| first sub |------+ +------------+ | xxxxx | +| name | +-------+ +| value | ++-----------+ + + + +Greatly simplyfied structure of the nt-registry: +================================================ + + + +-------------------------------------------------------------------------+ + v | ++---------------+ +-------------> +-----------+ +------> +---------+ | +| "nk" | | | lf-rec. | | | nk-rec. | | +| ID | | | # of keys | | | parent |---+ +| Date | | | 1st key |--+ | .... | +| parent | | +-----------+ +---------+ +| suk-keys |-------+ +| values |---------------------> +----------+ +| SK-rec. |---------------+ | 1. value |--> +----------+ +| class |--+ | +----------+ | vk-rec. | ++---------------+ | | | .... | + v | | data |--> +-------+ + +------------+ | +----------+ | xxxxx | + | Class name | | +-------+ + +------------+ | + v + +---------+ +---------+ + +-----> | next sk |---> | Next sk |--+ + | +---| prev sk | <---| prev sk | | + | | | .... | | ... | | + | | +---------+ +---------+ | + | | ^ | + | +--------------------+ | + +------------------------------------+ + +-------------------------------------------------------------------------------- + +Hope this helps.... (Although it was "fun" for me to uncover this things, + it took me several sleepless nights ;) + + B.D. diff --git a/chntpw-before.c b/chntpw-before.c new file mode 100644 index 0000000..a1043bb --- /dev/null +++ b/chntpw-before.c @@ -0,0 +1,1780 @@ +/* + * chntpw.c - Offline Password Edit Utility for NT 3.51 4.0 5.0 5.1 6.0 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. + * + * 2008-mar: 64 bit compatible patch set by NN + * 2007-sep: Group handling extended, promotion now public + * 2007-sep: User edit menu, some changes to user info edit + * 2007-apr-may: Get and display users group memberships + * 2007-apr: GNU license. Some bugfixes. Cleaned up some output. + * 2004-aug: More stuff in regedit. Stringinput bugfixes. + * 2004-jan: Changed some of the verbose/debug stuff + * 2003-jan: Changed to use more of struct based V + some small stuff + * 2003-jan: Support in ntreg for adding keys etc. Editor updated. + * 2002-dec: New option: Specify user using RID + * 2002-dec: New option: blank the pass (zero hash lengths). + * 2001-jul: extra blank password logic (when NT or LANMAN hash missing) + * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp + * 2000-jun: changing passwords regardless of syskey. + * 2000-jun: syskey disable works on NT4. Not properly on NT5. + * 2000-jan: Attempt to detect and disable syskey + * 1999-feb: Now able to browse registry hives. (write support to come) + * See HISTORY.txt for more detailed info on history. + * + ***** + * + * Copyright (c) 1997-2007 Petter Nordahl-Hagen. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See file GPL.txt for the full license. + * + ***** + * + * Information and ideas taken from pwdump by Jeremy Allison. + * + * More info from NTCrack by Jonathan Wilkins. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define uchar u_char +#define MD4Init MD4_Init +#define MD4Update MD4_Update +#define MD4Final MD4_Final + +#include "ntreg.h" +#include "sam.h" + +const char chntpw_version[] = "chntpw version 0.99.6 080320 (sixtyfour), (c) Petter N Hagen"; + +extern char *val_types[REG_MAX+1]; + +/* Global verbosity */ +int gverbose = 0; + + +#define MAX_HIVES 10 + +/* Array of loaded hives */ +struct hive *hive[MAX_HIVES+1]; +int no_hives = 0; + +/* Icky icky... globals used to refer to hives, will be + * set when loading, so that hives can be loaded in any order + */ + +int H_SAM = -1; +int H_SYS = -1; +int H_SEC = -1; +int H_SOF = -1; + +int syskeyreset = 0; +int dirty = 0; +int max_sam_lock = 0; + +/* + * of user with RID 500, because silly MS decided + * to localize the bloody admin-username!! AAAGHH! + */ +char admuser[129]="Administrator"; + +/* ============================================================== */ + +/* Crypto-stuff & support for what we'll do in the V-value */ + +/* Zero out string for lanman passwd, then uppercase + * the supplied password and put it in here */ + +void make_lanmpw(char *p, char *lm, int len) +{ + int i; + + for (i=0; i < 15; i++) lm[i] = 0; + for (i=0; i < len; i++) lm[i] = toupper(p[i]); +} + +/* + * Convert a 7 byte array into an 8 byte des key with odd parity. + */ + +void str_to_key(unsigned char *str,unsigned char *key) +{ + int i; + + key[0] = str[0]>>1; + key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); + key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); + key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); + key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); + key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); + key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); + key[7] = str[6]&0x7F; + for (i=0;i<8;i++) { + key[i] = (key[i]<<1); + } + DES_set_odd_parity((des_cblock *)key); +} + +/* + * Function to convert the RID to the first decrypt key. + */ + +void sid_to_key1(uint32_t sid,unsigned char deskey[8]) +{ + unsigned char s[7]; + + s[0] = (unsigned char)(sid & 0xFF); + s[1] = (unsigned char)((sid>>8) & 0xFF); + s[2] = (unsigned char)((sid>>16) & 0xFF); + s[3] = (unsigned char)((sid>>24) & 0xFF); + s[4] = s[0]; + s[5] = s[1]; + s[6] = s[2]; + + str_to_key(s,deskey); +} + +/* + * Function to convert the RID to the second decrypt key. + */ + +void sid_to_key2(uint32_t sid,unsigned char deskey[8]) +{ + unsigned char s[7]; + + s[0] = (unsigned char)((sid>>24) & 0xFF); + s[1] = (unsigned char)(sid & 0xFF); + s[2] = (unsigned char)((sid>>8) & 0xFF); + s[3] = (unsigned char)((sid>>16) & 0xFF); + s[4] = s[0]; + s[5] = s[1]; + s[6] = s[2]; + + str_to_key(s,deskey); +} + +/* DES encrypt, for LANMAN */ + +void E1(uchar *k, uchar *d, uchar *out) +{ + des_key_schedule ks; + des_cblock deskey; + + str_to_key(k,(uchar *)deskey); +#ifdef __FreeBSD__ + des_set_key(&deskey,ks); +#else /* __FreeBsd__ */ + des_set_key((des_cblock *)deskey,ks); +#endif /* __FreeBsd__ */ + des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT); +} + + +/* Check if hive is SAM, and if it is, extract some + * global policy information from it, like lockout counts etc + */ + +void check_get_samdata(void) +{ + struct accountdb_F *f; + struct keyval *v; + + if (H_SAM >= 0) { + + /* Get users F value */ + v = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK); + if (!v) { + printf("Login counts data not found in SAM\n"); + return; + } + printf("\n* SAM policy limits:\n"); + + f = (struct accountdb_F *)&v->data; + max_sam_lock = f->locklimit; + + printf("Failed logins before lockout is: %d\n",max_sam_lock); + printf("Minimum password length : %d\n",f->minpwlen); + printf("Password history count : %d\n",f->minpwlen); + + } +} + + +/* Try to decode and possibly change account lockout etc + * This is \SAM\Domains\Account\Users\\F + * It's size seems to always be 0x50. + * Params: RID - user ID, mode - 0 silent, 1 silent, 2 edit. + * Returns: ACB bits with high bit set if lockout count is >0 + */ + +short handle_F(int rid, int mode) +{ + + struct user_F *f; + char s[200]; + struct keyval *v; + unsigned short acb; + int b; + + if (H_SAM < 0) return(0); + + /* Get users F value */ + snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid); + v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); + if (!v) { + printf("Cannot find value <%s>\n",s); + return(0); + } + + if (v->len < 0x48) { + printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len); + FREE(v); + return(0); + } + + f = (struct user_F *)&v->data; + acb = f->ACB_bits; + + if (mode == 1) { + printf("Account bits: 0x%04x =\n",acb); + + + for (b=0; b < 15; b++) { + printf("[%s] %-15.15s | ", + (acb & (1<failedcnt,max_sam_lock); + printf("Total login count: %u\n",f->logins); + } + + if (mode == 2) { + acb |= ACB_PWNOEXP; + acb &= ~ACB_DISABLED; + acb &= ~ACB_AUTOLOCK; + f->ACB_bits = acb; + f->failedcnt = 0; + put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY,TPF_VK_EXACT); + printf("Unlocked!\n"); + } + return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15); +} + + +/* List users membership or check if admin (is in admin group) + * rid - users rid + * check - if 1 just check if admin, do not list + * returns true if user is admin + */ + +int list_user_groups(int rid, int check) +{ + char s[200]; + char g[200]; + char groupname[128]; + int nk = 0; + struct keyval *m = NULL, *c = NULL; + struct group_C *cd; + unsigned int *grps; + int count = 0, isadmin = 0; + int i, size, grp, grpnamoffs, grpnamlen; + + if (!rid || (H_SAM < 0)) return(0); + + + /* Get member list for user. Go for the first full SID, it's usually local computer I hope */ + snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\S-1-5-21-\\%08X",rid); + /* Now, the TYPE field is the number of groups the user is member of */ + /* Don't we just love the inconsistent use of fields!! */ + nk = trav_path(hive[H_SAM], 0, s, 0); + if (!nk) { + /* This probably means user is not in any group. Seems to be the case + for a couple of XPs built in support / guest users. So just return */ + if (gverbose) printf("list_user_groups(): Cannot find RID under computer SID <%s>\n",s); + return(0); + } + nk += 4; + count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT); + if (count == -1) { + printf("list_user_groups(): Cannot find value <%s\\@>\n",s); + return(0); + } + + if (!check) printf("User is member of %d groups:\n",count); + + /* This is the data size */ + size = get_val_len(hive[H_SAM],nk,"@",TPF_VK_EXACT); + + /* It should be 4 bytes for each group */ + if (gverbose) printf("Data size %d bytes.\n",size); + if (size != count * 4) { + printf("list_user_groups(): DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); + } + + m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK_EXACT); + if (!m) { + printf("list_user_groups(): Could not get value data! Giving up.\n"); + return(0); + } + + grps = (unsigned int *)&m->data; + for (i = 0; i < count; i++) { + grp = grps[i]; + if (!check) printf("%08x ",grp); + + if (grp == 0x220) isadmin = 1; + + if (!check) { + snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); + c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT); + if (c) { + cd = (struct group_C *)&c->data; + grpnamoffs = cd->grpname_ofs + 0x34; + grpnamlen = cd->grpname_len; + + cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen); + + printf("= %s (which has %d members)\n",groupname,cd->grp_members); + + } else { + printf("Group info for %x not found!\n",grp); + } + } + } + return(isadmin); +} + + + +/* Promote user into administrators group (group ID 0x220) + * And remove from all others... + * rid - users rid + * no returns yet + * THIS IS VERY HACKISH YET + */ + +void promote_user(int rid) +{ + char s[200]; + char g[200]; + int nk = 0; + struct keyval *m = NULL, *c = NULL; + struct keyval admember = { 4, 0x220 }; + unsigned int *grps, *gcnts; + int count = 0; + int i, size, grp; + + if (!rid || (H_SAM < 0)) return; + + + /* Get member list for user. Go for the first full SID, it's usually local computer I hope */ + snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\S-1-5-21-\\%08X",rid); + /* Now, the TYPE field is the number of groups the user is member of */ + /* Don't we just love the inconsistent use of fields!! */ + nk = trav_path(hive[H_SAM], 0, s, 0); + if (!nk) { + printf("Cannot find path <%s>\n",s); + return; + } + nk += 4; + count = get_val_type(hive[H_SAM],nk,"@",TPF_VK); + if (count == -1) { + printf("Cannot find value <%s\\@>\n",s); + return; + } + printf("User is member of %d groups.\n",count); + + /* This is the data size */ + size = get_val_len(hive[H_SAM],nk,"@",TPF_VK); + + /* It should be 4 bytes for each group */ + printf("Data size %d bytes.\n",size); + if (size != count * 4) { + printf("DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); + } + + m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK); + if (!m) { + printf("Could not get value data! Giving up.\n"); + return; + } + + printf("User was member of groups: "); + grps = (unsigned int *)&m->data; + for (i = 0; i < count; i++) { + grp = grps[i]; + printf("%08x ",grp); + switch (grp) { + case 0x220: printf("=Administrators, "); break; + case 0x221: printf("=Users, "); break; + case 0x222: printf("=Guests, "); break; + default: printf(", "); break; + } + snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); + c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK); + if (c) { + gcnts = (unsigned int *)&c->data; + gcnts[0xc]--; + /* Decrease members counter */ + put_buf2val(hive[H_SAM], c, 0, g, 0, TPF_VK); + } else { + printf("Group info for %x not found!\n",grp); + } + } +#if 1 + printf("\nDeleting user memberships\n"); + + del_value(hive[H_SAM], nk, "@", TPF_VK); + + printf("Adding into only administrators:\n"); + + if (!add_value(hive[H_SAM], nk, "@", 1)) { /* Type is # of groups, here 1 */ + printf("Failed to add @ value to key\n"); + } +#endif + put_buf2val(hive[H_SAM], &admember, nk, "@", 0, TPF_VK); + + /* Now bumb up administrator groups count */ + c = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Builtin\\Aliases\\00000220\\C", 0, TPF_VK); + if (!c) printf("Group info for 220 (adm) not found!\n"); + gcnts = (unsigned int *)&c->data; + gcnts[0xc]++; + put_buf2val(hive[H_SAM], c, 0, "\\SAM\\Domains\\Builtin\\Aliases\\00000220\\C", 0, TPF_VK); + + printf("Promotion DONE!\n"); + +} + + + + +/* Decode the V-struct, and change the password + * vofs - offset into SAM buffer, start of V struct + * rid - the users RID, required for the DES decrypt stage + * + * Some of this is ripped & modified from pwdump by Jeremy Allison + * + */ +char *change_pw(char *buf, int rid, int vlen, int stat) +{ + + uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25}; + char yn[4]; + int pl; + char *vp; + static char username[128],fullname[128]; + char comment[128],homedir[128],md4[32],lanman[32]; + char newunipw[34], newp[20], despw[20], newlanpw[16], newlandes[20]; + int username_offset,username_len; + int fullname_offset,fullname_len; + int comment_offset,comment_len; + int homedir_offset,homedir_len; + int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs,i; + int dontchange = 0; + struct user_V *v; + + des_key_schedule ks1, ks2; + des_cblock deskey1, deskey2; + + MD4_CTX context; + unsigned char digest[16]; + unsigned short acb; + + v = (struct user_V *)buf; + vp = buf; + + username_offset = v->username_ofs; + username_len = v->username_len; + fullname_offset = v->fullname_ofs; + fullname_len = v->fullname_len; + comment_offset = v->comment_ofs; + comment_len = v->comment_len; + homedir_offset = v->homedir_ofs; + homedir_len = v->homedir_len; + lmpw_offs = v->lmpw_ofs; + lmpw_len = v->lmpw_len; + ntpw_offs = v->ntpw_ofs; + ntpw_len = v->ntpw_len; + + if (!rid) { + printf("No RID given. Unable to change passwords..\n"); + return(0); + } + + if (gverbose) { + printf("lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len); + printf("ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len); + } + + *username = 0; + *fullname = 0; + *comment = 0; + *homedir = 0; + + if(username_len <= 0 || username_len > vlen || + username_offset <= 0 || username_offset >= vlen || + comment_len < 0 || comment_len > vlen || + fullname_len < 0 || fullname_len > vlen || + homedir_offset < 0 || homedir_offset >= vlen || + comment_offset < 0 || comment_offset >= vlen || + lmpw_offs < 0 || lmpw_offs >= vlen) + { + if (stat != 1) printf("change_pw: Not a legal V struct? (negative struct lengths)\n"); + return(NULL); + } + + /* Offsets in top of struct is relative to end of pointers, adjust */ + username_offset += 0xCC; + fullname_offset += 0xCC; + comment_offset += 0xCC; + homedir_offset += 0xCC; + ntpw_offs += 0xCC; + lmpw_offs += 0xCC; + + cheap_uni2ascii(vp + username_offset,username,username_len); + cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len); + cheap_uni2ascii(vp + comment_offset,comment,comment_len); + cheap_uni2ascii(vp + homedir_offset,homedir,homedir_len); + +#if 0 + /* Reset hash-lengths to 16 if syskey has been reset */ + if (syskeyreset && ntpw_len > 16 && !stat) { + ntpw_len = 16; + lmpw_len = 16; + ntpw_offs -= 4; + (unsigned int)*(vp+0xa8) = ntpw_offs - 0xcc; + *(vp + 0xa0) = 16; + *(vp + 0xac) = 16; + } +#endif + + if (stat) { + acb = handle_F(rid,0); + printf("| %04x | %-30.30s | %-6s | %-8s |\n", + rid, username, (list_user_groups(rid,1) ? "ADMIN" : "") , ( acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "") ); + return(username); + } + + printf("\nRID : %04d [%04x]\n",rid,rid); + printf("Username: %s\n",username); + printf("fullname: %s\n",fullname); + printf("comment : %s\n",comment); + printf("homedir : %s\n\n",homedir); + + list_user_groups(rid,0); + printf("\n"); + + acb = handle_F(rid,1); + + if (lmpw_len < 16 && gverbose) { + printf("** LANMAN password not set. User MAY have a blank password.\n** Usually safe to continue. Normal in Vista\n"); + } + + if (ntpw_len < 16) { + printf("** No NT MD4 hash found. This user probably has a BLANK password!\n"); + if (lmpw_len < 16) { + printf("** No LANMAN hash found either. Sorry, cannot change. Try login with no password!\n"); + dontchange = 1; + } else { + printf("** LANMAN password IS however set. Will now install new password as NT pass instead.\n"); + printf("** NOTE: Continue at own risk!\n"); + ntpw_offs = lmpw_offs; + *(vp+0xa8) = ntpw_offs - 0xcc; + ntpw_len = 16; + lmpw_len = 0; + } + } + + if (gverbose) { + hexprnt("Crypted NT pw: ",(unsigned char *)(vp+ntpw_offs),16); + hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16); + } + + /* Get the two decrpt keys. */ + sid_to_key1(rid,(unsigned char *)deskey1); + des_set_key((des_cblock *)deskey1,ks1); + sid_to_key2(rid,(unsigned char *)deskey2); + des_set_key((des_cblock *)deskey2,ks2); + + /* Decrypt the NT md4 password hash as two 8 byte blocks. */ + des_ecb_encrypt((des_cblock *)(vp+ntpw_offs ), + (des_cblock *)md4, ks1, DES_DECRYPT); + des_ecb_encrypt((des_cblock *)(vp+ntpw_offs + 8), + (des_cblock *)&md4[8], ks2, DES_DECRYPT); + + /* Decrypt the lanman password hash as two 8 byte blocks. */ + des_ecb_encrypt((des_cblock *)(vp+lmpw_offs), + (des_cblock *)lanman, ks1, DES_DECRYPT); + des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8), + (des_cblock *)&lanman[8], ks2, DES_DECRYPT); + + if (gverbose) { + hexprnt("MD4 hash : ",(unsigned char *)md4,16); + hexprnt("LANMAN hash : ",(unsigned char *)lanman,16); + } + + printf("\n- - - - User Edit Menu:\n"); + printf(" 1 - Clear (blank) user password\n" + " 2 - Edit (set new) user password (careful with this on XP or Vista)\n" + " 3 - Promote user (make user an administrator)\n"); + printf("%s4 - Unlock and enable user account%s\n", (acb & 0x8000) ? " " : "(", + (acb & 0x8000) ? " [probably locked now]" : ") [seems unlocked already]"); + printf(" q - Quit editing user, back to user select\n"); + + pl = fmyinput("Select: [q] > ",newp,16); + + if ( (pl < 1) || (*newp == 'q') || (*newp == 'Q')) return(0); + + if (*newp == '3') { + printf("NOTE: This function is still experimental, and in some cases it\n" + " may result in stangeness when editing user/group in windows.\n" + " Also, users (like Guest often is) may still be prevented\n" + " from login via security/group policies which is not changed.\n"); + fmyinput("Do you still want to promote the user? (y/n) [n] ",yn,2); + if (*yn == 'y' || *yn == 'Y') { + promote_user(rid); + } + return(username); + } + + if (*newp == '4') { + acb = handle_F(rid,2); + return(username); + } + + if (*newp == '2') { + + if (dontchange) { + printf("Sorry, unable to edit since password seems blank already (thus no space for it)\n"); + return(0); + } + + pl = fmyinput("New Password: ",newp,16); + + if (pl < 1) { + printf("No change.\n"); + return(0); + } + + cheap_ascii2uni(newp,newunipw,pl); + + make_lanmpw(newp,newlanpw,pl); + + /* printf("Ucase Lanman: %s\n",newlanpw); */ + + MD4Init (&context); + MD4Update (&context, newunipw, pl<<1); + MD4Final (digest, &context); + + if (gverbose) hexprnt("\nNEW MD4 hash : ",digest,16); + + E1((uchar *)newlanpw, x1, (uchar *)lanman); + E1((uchar *)newlanpw+7, x1, (uchar *)lanman+8); + + if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16); + + /* Encrypt the NT md4 password hash as two 8 byte blocks. */ + des_ecb_encrypt((des_cblock *)digest, + (des_cblock *)despw, ks1, DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)(digest+8), + (des_cblock *)&despw[8], ks2, DES_ENCRYPT); + + des_ecb_encrypt((des_cblock *)lanman, + (des_cblock *)newlandes, ks1, DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)(lanman+8), + (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT); + + if (gverbose) { + hexprnt("NEW DES crypt : ",(unsigned char *)despw,16); + hexprnt("NEW LANMAN crypt: ",(unsigned char *)newlandes,16); + } + + /* Reset hash length to 16 if syskey enabled, this will cause + * a conversion to syskey-hashes upon next boot */ + if (syskeyreset && ntpw_len > 16) { + ntpw_len = 16; + lmpw_len = 16; + ntpw_offs -= 4; + *(vp+0xa8) = (unsigned int)(ntpw_offs - 0xcc); + *(vp + 0xa0) = 16; + *(vp + 0xac) = 16; + } + + for (i = 0; i < 16; i++) { + *(vp+ntpw_offs+i) = (unsigned char)despw[i]; + if (lmpw_len >= 16) *(vp+lmpw_offs+i) = (unsigned char)newlandes[i]; + } + + printf("Password changed!\n"); + + + } /* new password */ + + else if (pl == 1 && *newp == '1') { + /* Setting hash lengths to zero seems to make NT think it is blank + * However, since we cant cut the previous hash bytes out of the V value + * due to missing resize-support of values, it may leak about 40 bytes + * each time we do this. + */ + v->ntpw_len = 0; + v->lmpw_len = 0; + + printf("Password cleared!\n"); + } + +#if 0 + hexprnt("Pw in buffer: ",(vp+ntpw_offs),16); + hexprnt("Lm in buffer: ",(vp+lmpw_offs),16); +#endif + dirty = 1; + return(username); +} + + +/* Here we put our knowledge to use, basic routines to + * decode and display registry contents almost like a filesystem + */ + +/* display (cat) the value, + * vofs = offset to 'nk' node, paths relative to this (or 0 for root) + * path = path string to value + * Does not handle all types yet (does a hexdump instead) + * MULTI_SZ (multi unicode-string) - only displays first string, + * but also does a hexdump. + */ +void cat_vk(struct hive *hdesc, int nkofs, char *path, int dohex) +{ + void *data; + int len,i,type; + char string[SZ_MAX+1]; + + type = get_val_type(hdesc, nkofs, path, 0); + if (type == -1) { + printf("cat_vk: No such value <%s>\n",path); + return; + } + + len = get_val_len(hdesc, nkofs, path, 0); + if (!len) { + printf("cat_vk: Value <%s> has zero length\n",path); + return; + } + + data = (void *)get_val_data(hdesc, nkofs, path, 0, 0); + if (!data) { + printf("cat_vk: Value <%s> references NULL-pointer (bad boy!)\n",path); + abort(); + return; + } + + printf("Value <%s> of type %s, data length %d [0x%x]\n", path, + (type < REG_MAX ? val_types[type] : "(unknown)"), len, len); + + if (dohex) type = REG_BINARY; + switch (type) { + case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + 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); + break; + case REG_DWORD: + printf("0x%08x",*(unsigned short *)data); + break; + default: + printf("Don't know how to handle type yet!\n"); + case REG_BINARY: + hexdump((char *)data, 0, len, 1); + } + putchar('\n'); +} + +/* =================================================================== */ + +/* Registry editor frontend */ + +struct cmds { + char cmd_str[12]; + int cmd_num; +}; + +#define MCMD_CD 1 +#define MCMD_LS 2 +#define MCMD_QUIT 3 +#define MCMD_CAT 4 +#define MCMD_STRUCT 5 +#define MCMD_DEBUG 6 +#define MCMD_HELP 7 +#define MCMD_EXPORTKEY 8 +#define MCMD_HIVE 9 +#define MCMD_EDIT 10 +#define MCMD_ALLOC 11 +#define MCMD_FREE 12 +#define MCMD_ADDV 13 +#define MCMD_DELV 14 +#define MCMD_DELVALL 15 +#define MCMD_NEWKEY 16 +#define MCMD_DELKEY 17 +#define MCMD_CATHEX 18 +#define MCMD_RDEL 19 +#define MCMD_CK 20 + +struct cmds maincmds[] = { + { "cd" , MCMD_CD } , + { "ls" , MCMD_LS } , + { "dir", MCMD_LS } , + { "q" , MCMD_QUIT } , + { "cat", MCMD_CAT } , + { "type",MCMD_CAT } , + { "st" , MCMD_STRUCT } , + { "debug", MCMD_DEBUG } , + { "hive", MCMD_HIVE } , + { "ed", MCMD_EDIT } , +#if ALLOC_DEBUG + { "alloc", MCMD_ALLOC } , + { "free", MCMD_FREE } , +#endif + { "nv", MCMD_ADDV } , + { "dv", MCMD_DELV } , + { "delallv", MCMD_DELVALL } , + { "nk", MCMD_NEWKEY } , + { "dk", MCMD_DELKEY } , + { "hex", MCMD_CATHEX } , + { "rdel", MCMD_RDEL } , + { "ek", MCMD_EXPORTKEY }, + { "ck", MCMD_CK } , + { "?", MCMD_HELP } , + { "", 0 } +}; + +/* Edit value: Invoke whatever is needed to edit it + * based on its type + */ + +void edit_val(struct hive *h, int nkofs, char *path) +{ + struct keyval *kv, *newkv; + int type,len,n,i,in,go, newsize, d = 0, done, insert = 0; + char inbuf[SZ_MAX+4]; + char origstring[SZ_MAX+4]; + char *newstring; + char *dbuf; + + type = get_val_type(h, nkofs, path, TPF_VK); + if (type == -1) { + printf("Value <%s> not found!\n",path); + return; + } + + kv = get_val2buf(h, NULL, nkofs, path, type, TPF_VK); + if (!kv) { + printf("Unable to get data of value <%s>\n",path); + return; + } + len = kv->len; + + printf("EDIT: <%s> of type %s with length %d [0x%x]\n", path, + (type < REG_MAX ? val_types[type] : "(unknown)"), + len, len); + + switch(type) { + case REG_DWORD: + printf("DWORD: Old value %d [0x%x], ", kv->data, kv->data); + fmyinput("enter new value (prepend 0x if hex, empty to keep old value)\n-> ", + inbuf, 12); + if (*inbuf) { + sscanf(inbuf,"%i",&kv->data); + d = 1; + } + printf("DWORD: New value %d [0x%x], ", kv->data, kv->data); + break; + case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + newstring = NULL; + dbuf = (char *)&kv->data; + cheap_uni2ascii(dbuf,origstring,len); + n = 0; i = 0; + while (i < (len>>1)-1) { + printf("[%2d]: %s\n",n,origstring+i); + i += strlen(origstring+i) + 1; + n++; + } + + printf("\nNow enter new strings, one by one.\n"); + printf("Enter nothing to keep old.\n"); + if (type == REG_MULTI_SZ) { + printf("'--n' to quit (remove rest of strings)\n"); + printf("'--i' insert new string at this point\n"); + printf("'--q' to quit (leaving remaining strings as is)\n"); + printf("'--Q' to quit and discard all changes\n"); + printf("'--e' for empty string in this position\n"); + } + n = 0; i = 0; in = 0; go = 0; done = 0; + + /* Now this one is RATHER UGLY :-} */ + + while (i < (len>>1)-1 || !done) { + + printf("[%2d]: %s\n",n, insert == 1 ? "[INSERT]" : ((i < (len>>1)-1 ) ? origstring+i : "[NEW]")); + if (insert) insert++; + if (!go) fmyinput("-> ",inbuf, 500); + else *inbuf = 0; + if (*inbuf && strcmp("--q", inbuf)) { + if (!strcmp("--n", inbuf) || !strcmp("--Q", inbuf)) { /* Zap rest */ + i = (len>>1) ; done = 1; + } else if (strcmp("--i", inbuf)) { /* Copy out given string */ + if (!strcmp("--e",inbuf)) *inbuf = '\0'; + if (newstring) newstring = realloc(newstring, in+strlen(inbuf)+1); + else newstring = malloc(in+strlen(inbuf)+1); + strcpy(newstring+in, inbuf); + in += strlen(inbuf)+1; + } else { + insert = 1; + } + } else { /* Copy out default string */ + + if (newstring) newstring = realloc(newstring, in+strlen(origstring+i)+1); + else newstring = malloc(in + strlen(origstring+i) + 1); + strcpy(newstring+in, origstring+i); + in += strlen(origstring+i)+1; + + if (!strcmp("--q", inbuf)) { + go = 1; done = 1; + if (!(i < (len>>1)-1 )) { + in--; /* remove last empty if in NEW-mode */ + } + } + } + + if (!insert) i += strlen(origstring+i) + 1; + if (insert != 1) n++; + if (insert == 2) insert = 0; + if (type != REG_MULTI_SZ) { + i = (len<<1); + done = 1; + } + + } + + if (strcmp("--Q", inbuf)) { /* We didn't bail out */ + if (newstring) newstring = realloc(newstring, in+1); + else newstring = malloc(in+1); + if (type == REG_MULTI_SZ) { + in++; + *(newstring+in) = '\0'; /* Must add null termination */ + } + ALLOC(newkv,1,(in<<1)+sizeof(int)); + newkv->len = in<<1; + printf("newkv->len: %d\n",newkv->len); + cheap_ascii2uni(newstring, (char *)&(newkv->data), in); + + d = 1; + + FREE(kv); + kv = newkv; + + } + break; + + default: + printf("Type not handeled (yet), invoking hex editor on data!\n"); + case REG_BINARY: + fmyinput("New length (ENTER to keep same): ",inbuf,90); + if (*inbuf) { + 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)); + FREE(kv); + kv = newkv; + kv->len = newsize; + } + d = debugit((char *)&kv->data, kv->len); + break; + } + + if (d) { + if (!(put_buf2val(h, kv, nkofs, path, type, TPF_VK))) { + printf("Failed to set value!?\n"); + } + } + FREE(kv); +} + +/* look up command in array + */ +int parsecmd(char **s, struct cmds *cmd) +{ + + int l = 0; + + while ((*s)[l] && ((*s)[l] != ' ')) { + l++; + } + while (cmd->cmd_num) { + if (!strncmp(*s, cmd->cmd_str, l)) { + *s += l; + return(cmd->cmd_num); + } + cmd++; + } + return(0); +} + +/* Simple interactive command-parser + * Main loop for manually looking through the registry + */ + +void mainloop(void) +{ + struct hive *hdesc; + int cdofs, newofs; + struct nk_key *cdkey; + char inbuf[100], *bp, *file, *prefix; + char path[1000]; + int l, vkofs, nh, i; + int usehive = 0; + struct keyval *kv; + +#if ALLOC_DEBUG + int pagestart; + int freetest; +#endif + + hdesc = hive[usehive]; + cdofs = hdesc->rootofs; + + printf("Simple registry editor. ? for help.\n"); + + while (1) { + cdkey = (struct nk_key *)(hdesc->buffer + cdofs); + + *path = 0; + get_abs_path(hdesc,cdofs+4, path, 50); + +#if ALLOC_DEBUG + pagestart = find_page_start(hdesc,cdofs); + printf("find_page_start: 0x%x\n",pagestart); + freetest = find_free_blk(hdesc,pagestart,10); + printf("find_free_blk: 0x%x\n",freetest); +#endif + if (gverbose) printf("\n[%0x] %s> ",cdofs,path); + else printf("\n%s> ",path); + l = fmyinput("",inbuf,90); + bp = inbuf; + skipspace(&bp); + + if (l > 0 && *bp) { + switch(parsecmd(&bp,maincmds)) { + case MCMD_HELP: + printf("Simple registry editor:\n"); + printf("hive [] - list loaded hives or switch to hive numer n\n"); + printf("cd - change current key\n"); + printf("ls | dir [] - show subkeys & values,\n"); + printf("cat | type - show key 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"); + printf("dk - delete key (must be empty)\n"); + printf("ed - Edit value\n"); + printf("nv - Add value\n"); + printf("dv - Delete value\n"); + printf("delallv - Delete all values in current key\n"); + printf("rdel - Recursively delete key & subkeys\n"); + printf("ek - export key to (Windows .reg file format)\n"); + printf("debug - enter buffer hexeditor\n"); + printf("st [] - debug function: show struct info\n"); + printf("q - quit\n"); + break; + + case MCMD_DELKEY : + bp++; + skipspace(&bp); + del_key(hdesc, cdofs + 4, bp); + break; + case MCMD_NEWKEY : + bp++; + skipspace(&bp); + add_key(hdesc, cdofs + 4, bp); + break; + case MCMD_DELVALL : + bp++; + skipspace(&bp); + del_allvalues(hdesc, cdofs + 4); + break; + case MCMD_DELV : + bp++; + skipspace(&bp); + del_value(hdesc, cdofs + 4, bp, 0); + break; + case MCMD_ADDV : + bp++; + skipspace(&bp); + nh = gethex(&bp); + skipspace(&bp); + add_value(hdesc, cdofs+4, bp, nh); + break; +#ifdef ALLOC_DEBUG + case MCMD_FREE : + bp++; + skipspace(&bp); + nh = gethex(&bp); + free_block(hdesc, nh); + break; + case MCMD_ALLOC : + bp++; + skipspace(&bp); + nh = gethex(&bp); + alloc_block(hdesc, cdofs+4, nh); + break; +#endif + case MCMD_LS : + bp++; + skipspace(&bp); + nk_ls(hdesc, bp, cdofs+4, 0); + break; + case MCMD_CK : + bp++; + skipspace(&bp); + kv = get_class(hdesc, cdofs+4, bp); + if (kv) { + hexdump((char *)&kv->data, 0, kv->len, 1); + FREE(kv); + } + break; + case MCMD_RDEL : + bp++; + skipspace(&bp); + rdel_keys(hdesc, bp, cdofs+4); + break; + case MCMD_EDIT : + bp++; + skipspace(&bp); + edit_val(hdesc, cdofs+4, bp); + break; + case MCMD_HIVE : + bp++; + skipspace(&bp); + if (*bp) { + nh = gethex(&bp); + if (nh >= 0 && nh < no_hives) { + usehive = nh; + printf("Switching to hive #%d, named <%s>, size %d [0x%x]\n", + usehive, hive[usehive]->filename, + hive[usehive]->size, + hive[usehive]->size); + hdesc = hive[usehive]; + cdofs = hdesc->rootofs; + } + } else { + for (nh = 0; nh < no_hives; nh++) { + printf("%c %c %2d %9d 0x%08x <%s>\n", (nh == usehive) ? '*' : ' ', + (hive[nh]->state & HMODE_DIRTY) ? 'D' : ' ', + nh, + hive[nh]->size, + hive[nh]->size, hive[nh]->filename); + } + } + break; + case MCMD_CD : + bp++; + skipspace(&bp); + newofs = trav_path(hdesc, cdofs+4,bp,TPF_NK); + if (newofs) cdofs = newofs; + else printf("Key %s not found!\n",bp); + break; + case MCMD_CAT: + bp++; + skipspace(&bp); + cat_vk(hdesc,cdofs+4,bp,0); + break; + case MCMD_CATHEX: + bp++; + skipspace(&bp); + cat_vk(hdesc,cdofs+4,bp,1); + break; + case MCMD_EXPORTKEY : + bp++; + skipspace(&bp); + file = bp; + i = 0; + while(*bp != ' ' && (*bp)) + { + i++; + bp++; + } + file[i] = '\0'; + bp++; + skipspace(&bp); + prefix = bp; + i = 0; + while(*bp != ' ' && (*bp)) + { + i++; + bp++; + } + prefix[i] = '\0'; + bp++; + skipspace(&bp); + export_key(hdesc, cdofs + 4, bp, file, prefix); + break; + case MCMD_STRUCT: + bp++; + skipspace(&bp); + vkofs = cdofs; + if (*bp) { + vkofs = gethex(&bp); + } + parse_block(hdesc,vkofs,1); + break; + case MCMD_DEBUG: + if (debugit(hdesc->buffer,hdesc->size)) hdesc->state |= HMODE_DIRTY; + break; + case MCMD_QUIT: + return; + break; + default: + printf("Unknown command: %s\n",bp); + break; + } + } + } +} + +/* List users in SAM file + * pageit - hmm.. forgot this one for this release.. + */ + +int list_users(int pageit) +{ + char s[200]; + struct keyval *v; + int nkofs /* ,vkofs */ ; + int rid; + int count = 0, countri = 0; + struct ex_data ex; + + if (H_SAM < 0) return(1); + nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Account\\Users\\Names\\",0); + if (!nkofs) { + printf("list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n"); + return(1); + } + + printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n"); + + while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) { + + /* 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); + 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); + if (!v) { + printf("Cannot find value <%s>\n",s); + return(1); + } + + if (v->len < 0xcc) { + printf("list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", + s, v->len); + } else { + change_pw( (char *)&v->data , rid, v->len, 1); + } + FREE(v); + FREE(ex.name); + } + return(0); +} + + +/* Find a username in the SAM registry, then get it's V-value, + * and feed it to the password changer. + */ + +void find_n_change(char *username) +{ + char s[200]; + struct vk_key *vkkey; + struct keyval *v; + int rid = 0; + + if ((H_SAM < 0) || (!username)) return; + if (*username == '0' && *(username+1) == 'x') sscanf(username,"%i",&rid); + + if (!rid) { /* Look up username */ + /* Extract the unnamed value out of the username-key, value is RID */ + snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",username); + rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT); + if (rid == -1) { + printf("Cannot find value <%s>\n",s); + return; + } + } + + /* + printf("Username: %s, RID = %d (0x%0x)\n",username,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); + if (!v) { + printf("Cannot find value <%s>\n",s); + return; + } + + 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); + } else { + change_pw( (char *)&v->data , rid, v->len, 0); + if (dirty) { + if (!(put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY, TPF_VK_EXACT))) { + printf("Failed to write updated <%s> to registry! Password change not completed!\n",s); + } + } + } + FREE(v); +} + +/* Check for presence of syskey and possibly disable it if + * user wants it. + * This is tricky, and extremely undocumented! + * See docs for more info on what's going on when syskey is installed + */ + +#undef LSADATA + +void handle_syskey(void) +{ + + /* This is \SAM\Domains\Account\F */ + struct samkeyf { + char unknown[0x50]; /* 0x0000 - Unknown. May be machine SID */ + char unknown2[0x14]; + char syskeymode; /* 0x0064 - Type/mode of syskey in use */ + char syskeyflags1[0xb]; /* 0x0065 - More flags/settings */ + char syskeyobf[0x30]; /* 0x0070 - This may very well be the obfuscated syskey */ + }; /* There may be more, usually 8 null-bytes? */ + + /* Security\Policy\SecretEncryptionKey\@, only on NT5 */ + /* Probably contains some keyinfo for syskey. Second DWORD seems to be syskeymode */ + struct secpoldata { + int unknown1; /* Some kind of flag? usually 1 */ + int syskeymode; /* Is this what we're looking for? */ + int unknown2; /* Usually 0? */ + char keydata[0x40]; /* Some kind of scrambled keydata? */ + }; + +#ifdef LSADATA + /* SYSTEM\CurrentControlSet\Control\Lsa\Data, only on NT5?? */ + /* Probably contains some keyinfo for syskey. Byte 0x34 seems to be mode */ + struct lsadata { + char keydata[0x34]; /* Key information */ + int syskeymode; /* Is this what we're looking for? */ + }; +#endif + + /* void *fdata; */ + struct samkeyf *ff = NULL; + struct secpoldata *sf = NULL; + /* struct lsadata *ld = NULL; */ + int /* len, */ i,secboot, samfmode, secmode /* , ldmode */ ; + struct keyval *samf, *secpol /* , *lsad */ ; + char *syskeytypes[4] = { "off", "key-in-registry", "enter-passphrase", "key-on-floppy" }; + char yn[5]; + + printf("\n---------------------> SYSKEY CHECK <-----------------------\n"); + + + if (H_SAM < 0) { + printf("ERROR: SAM hive not loaded!\n"); + return; + } + samf = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); + + if (samf && samf->len > 0x70 ) { + ff = (struct samkeyf *)&samf->data; + samfmode = ff->syskeymode; + } else { + samfmode = -1; + } + + secboot = -1; + if (H_SYS >= 0) { + secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT); + } + + secmode = -1; + if (H_SEC >=0) { + secpol = get_val2buf(hive[H_SEC], NULL, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_NONE, TPF_VK_EXACT); + if (secpol) { /* Will not be found in NT 4, take care of that */ + sf = (struct secpoldata *)&secpol->data; + secmode = sf->syskeymode; + } + } + +#ifdef LSADATA + lsad = get_val2buf(hive[H_SYS], NULL, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); + + if (lsad && lsad->len >= 0x38) { + ld = (struct lsadata *)&lsad->data; + ldmode = ld->syskeymode; + } else { + ldmode = -1; + } +#endif + + printf("SYSTEM SecureBoot : %d -> %s\n", secboot, + (secboot < 0 || secboot > 3) ? "Not Set (not installed, good!)" : syskeytypes[secboot]); + printf("SAM Account\\F : %d -> %s\n", samfmode, + (samfmode < 0 || samfmode > 3) ? "Not Set" : syskeytypes[samfmode]); + printf("SECURITY PolSecretEncryptionKey: %d -> %s\n", secmode, + (secmode < 0 || secmode > 3) ? "Not Set (OK if this is NT4)" : syskeytypes[secmode]); + +#ifdef LSADATA + printf("SYSTEM LsaData : %d -> %s\n\n", ldmode, + (ldmode < 0 || ldmode > 3) ? "Not Set (strange?)" : syskeytypes[ldmode]); +#endif + + if (secboot != samfmode && secboot != -1) { + printf("WARNING: Mismatch in syskey settings in SAM and SYSTEM!\n"); + printf("WARNING: It may be dangerous to continue (however, resetting syskey\n"); + printf(" may very well fix the problem)\n"); + } + + if (secboot > 0 || samfmode > 0) { + printf("\n***************** SYSKEY IS ENABLED! **************\n"); + printf("This installation very likely has the syskey passwordhash-obfuscator installed\n"); + printf("It's currently in mode = %d, %s-mode\n",secboot, + (secboot < 0 || secboot > 3) ? "Unknown" : syskeytypes[secboot]); + + if (no_hives < 2) { + printf("\nSYSTEM (and possibly SECURITY) hives not loaded, unable to disable syskey!\n"); + printf("Please start the program with at least SAM & SYSTEM-hive filenames as arguments!\n\n"); + return; + } + printf("SYSKEY is on! However, DO NOT DISABLE IT UNLESS YOU HAVE TO!\n"); + printf("This program can change passwords even if syskey is on, however\n"); + printf("if you have lost the key-floppy or passphrase you can turn it off,\n"); + printf("but please read the docs first!!!\n"); + printf("\n** IF YOU DON'T KNOW WHAT SYSKEY IS YOU DO NOT NEED TO SWITCH IT OFF!**\n"); + printf("NOTE: On WINDOWS 2000 it will not be possible\n"); + printf("to turn it on again! (and other problems may also show..)\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"); + + fmyinput("\nDo you really wish to disable SYSKEY? (y/n) [n] ",yn,2); + if (*yn == 'y') { + /* Reset SAM syskey infostruct, fill with zeroes */ + if (ff) { + ff->syskeymode = 0; + + for (i = 0; i < 0x3b; i++) { + ff->syskeyflags1[i] = 0; + } + + put_buf2val(hive[H_SAM], samf, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); + + } + /* Reset SECURITY infostruct (if any) */ + if (sf) { + memset(sf, 0, secpol->len); + sf->syskeymode = 0; + + put_buf2val(hive[H_SEC], secpol, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_BINARY, TPF_VK_EXACT); + + } + +#if LSADATA + if (ld) { + + ld->syskeymode = 0; + + put_buf2val(hive[H_SYS], lsad, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); + + } +#endif + + /* And SYSTEM SecureBoot parameter */ + + put_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT, 0); + + dirty = 1; + syskeyreset = 1; + printf("Updating passwordhash-lengths..\n"); + list_users(1); + printf("* SYSKEY RESET!\nNow please set new administrator password!\n"); + } else { + + syskeyreset = 1; + } + } else { + printf("Syskey not installed!\n"); + return; + } + +} + + +/* Interactive user edit */ +void useredit(void) +{ + char iwho[100]; + int il; + + printf("\n\n===== chntpw Edit User Info & Passwords ====\n\n"); + + if (H_SAM < 0) { + printf("ERROR: SAM registry file (which contains user data) is not loaded!\n\n"); + return; + } + + + list_users(1); + + while (1) { + printf("\nSelect: ! - quit, . - list users, 0x - User with RID (hex)\n"); + printf("or simply enter the username to change: [%s] ",admuser); + il = fmyinput("",iwho,32); + if (il == 1 && *iwho == '.') { printf("\n"); list_users(1); continue; } + if (il == 1 && *iwho == '!') return; + if (il == 0) strcpy(iwho,admuser); + find_n_change(iwho); + } + +} + + +void recoveryconsole() +{ + + int cmd = 0; + int sec = 0; + static char *scpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SetCommand"; + static char *slpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel"; + char yn[5]; + + if (H_SOF < 0) { + printf("\nSOFTWARE-hive not loaded, and there's where RecoveryConsole settings are..\n"); + return; + } + + cmd = get_dword(hive[H_SOF],0,scpath,TPF_VK_EXACT); + 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"); + return; + } + + printf("\nRecoveryConsole:\n- Extended SET command is \t%s\n", cmd>0 ? "ENABLED (1)" : "DISABLED (0)"); + printf("- Administrator password login: %s\n", sec>0 ? "SKIPPED (1)" : "ENFORCED (0)"); + + fmyinput("\nDo you want to change it? (y/n) [n] ",yn,2); + if (*yn == 'y') { + cmd ^= 1; + sec ^= 1; + if (!put_dword(hive[0], 0, scpath, TPF_VK_EXACT, cmd)) printf("Update of SET level failed registry edit\n"); + if (!put_dword(hive[0], 0, slpath, TPF_VK_EXACT, sec)) printf("Update of login level failed registry edit\n"); + printf("Done!\n"); + } + +} + + +/* Interactive menu system */ + +void interactive(void) +{ + int il; + char inbuf[20]; + + while(1) { + printf("\n\n<>========<> chntpw Main Interactive Menu <>========<>\n\n" + "Loaded hives:"); + 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" + " 9 - Registry editor, now with full write support!\n" + " q - Quit (you will be asked if there is something to save)\n" + "\n\n"); + + il = fmyinput("What to do? [1] -> ", inbuf, 10); + + if (!il) useredit(); + if (il) { + switch(inbuf[0]) { + case '1': useredit(); break; + case '2': handle_syskey(); break; + case '3': recoveryconsole(); break; + case '9': mainloop(); break; + case 'q': return; break; + } + } + } +} + + + +void usage(void) { + printf("chntpw: change password of a user in a NT/2k/XP/2k3/Vista SAM file, or invoke registry editor.\n" + "chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...]\n" + " -h This message\n" + " -u Username to change, Administrator is default\n" + " -l list all users in SAM file\n" + " -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" + "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" + ); +} + +int main(int argc, char **argv) +{ + + int dodebug = 0, list = 2, inter = 0,edit = 0,il,d = 0, dd = 0, logchange = 0, mode = 0; + extern int /* opterr, */ optind; + extern char* optarg; + char *filename,c; + char *who = "Administrator"; + char iwho[100]; + FILE *ch; /* Write out names of touched files to this */ + + char *options = "LNidehltvu:"; + + printf("%s\n",chntpw_version); + while((c=getopt(argc,argv,options)) > 0) { + switch(c) { + case 'd': dodebug = 1; break; + case 'e': edit = 1; break; + case 'L': logchange = 1; break; + case 'N': mode |= HMODE_NOALLOC; 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; + case 'h': usage(); exit(0); break; + default: usage(); exit(1); break; + } + } + filename=argv[optind]; + if (!filename || !*filename) { + usage(); exit(1); + } + do { + if (!(hive[no_hives] = openHive(filename, + HMODE_RW|mode))) { + printf("Unable to open/read a hive, exiting..\n"); + exit(1); + } + switch(hive[no_hives]->type) { + case HTYPE_SAM: H_SAM = no_hives; break; + case HTYPE_SOFTWARE: H_SOF = no_hives; break; + case HTYPE_SYSTEM: H_SYS = no_hives; break; + case HTYPE_SECURITY: H_SEC = no_hives; break; + } + no_hives++; + filename = argv[optind+no_hives]; + } while (filename && *filename && no_hives < MAX_HIVES); + + if (dodebug) debugit(hive[0]->buffer,hive[0]->size); + else { + + check_get_samdata(); + if (list && !edit && !inter) { + if ( list_users(1) ) edit = 1; + } + if (edit) mainloop(); + else if (who) { handle_syskey(); find_n_change(who); } + + if (inter) interactive(); + } + + if (list != 1) { + 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); + 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("Write hive files? (y/n) [n] : ",iwho,3); + if (*iwho == 'y' || logchange) { + if (logchange) { + ch = fopen("/tmp/changed","w"); + } + for (il = 0; il < no_hives; il++) { + if (hive[il]->state & HMODE_DIRTY) { + printf("%2d <%s> - ",il,hive[il]->filename); + if (!writeHive(hive[il])) { + printf("OK\n"); + if (logchange) fprintf(ch,"%s ",hive[il]->filename); + dd = 2; + } + } + } + if (logchange) { + fprintf(ch,"\n"); + fclose(ch); + } + } else { + printf("Not written!\n\n"); + } + } else { + printf("None!\n\n"); + } + } /* list only check */ + return(dd); +} diff --git a/chntpw.c b/chntpw.c new file mode 100644 index 0000000..06e7ac1 --- /dev/null +++ b/chntpw.c @@ -0,0 +1,1301 @@ +/* + * chntpw.c - Offline Password Edit Utility for Windows SAM database + * + * 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 + * http://bugs.gentoo.org/show_bug.cgi?id=185411 + * 2007-sep: Group handling extended, promotion now public + * 2007-sep: User edit menu, some changes to user info edit + * 2007-apr-may: Get and display users group memberships + * 2007-apr: GNU license. Some bugfixes. Cleaned up some output. + * 2004-aug: More stuff in regedit. Stringinput bugfixes. + * 2004-jan: Changed some of the verbose/debug stuff + * 2003-jan: Changed to use more of struct based V + some small stuff + * 2003-jan: Support in ntreg for adding keys etc. Editor updated. + * 2002-dec: New option: Specify user using RID + * 2002-dec: New option: blank the pass (zero hash lengths). + * 2001-jul: extra blank password logic (when NT or LANMAN hash missing) + * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp + * 2000-jun: changing passwords regardless of syskey. + * 2000-jun: syskey disable works on NT4. Not properly on NT5. + * 2000-jan: Attempt to detect and disable syskey + * 1999-feb: Now able to browse registry hives. (write support to come) + * See HISTORY.txt for more detailed info on history. + * + ***** + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See file GPL.txt for the full license. + * + ***** + * + * Information and ideas taken from pwdump by Jeremy Allison. + * + * More info from NTCrack by Jonathan Wilkins. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define uchar u_char +#define MD4Init MD4_Init +#define MD4Update MD4_Update +#define MD4Final MD4_Final + +#include "ntreg.h" +#include "sam.h" + +const char chntpw_version[] = "chntpw version 0.99.6 110511 , (c) Petter N Hagen"; + +extern char *val_types[REG_MAX+1]; + +/* Global verbosity */ +int gverbose = 0; + + +#define MAX_HIVES 10 + +/* Array of loaded hives */ +struct hive *hive[MAX_HIVES+1]; +int no_hives = 0; + +/* Icky icky... globals used to refer to hives, will be + * set when loading, so that hives can be loaded in any order + */ + +int H_SAM = -1; +int H_SYS = -1; +int H_SEC = -1; +int H_SOF = -1; + +int syskeyreset = 0; +int dirty = 0; +int max_sam_lock = 0; + +/* + * of user with RID 500, because silly MS decided + * to localize the bloody admin-username!! AAAGHH! + */ +char admuser[129]="Administrator"; + +/* ============================================================== */ + +/* Crypto-stuff & support for what we'll do in the V-value */ + +/* Zero out string for lanman passwd, then uppercase + * the supplied password and put it in here */ + +void make_lanmpw(char *p, char *lm, int len) +{ + int i; + + for (i=0; i < 15; i++) lm[i] = 0; + for (i=0; i < len; i++) lm[i] = toupper(p[i]); +} + +/* + * Convert a 7 byte array into an 8 byte des key with odd parity. + */ + +void str_to_key(unsigned char *str,unsigned char *key) +{ + int i; + + key[0] = str[0]>>1; + key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); + key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); + key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); + key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); + key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); + key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); + key[7] = str[6]&0x7F; + for (i=0;i<8;i++) { + key[i] = (key[i]<<1); + } + DES_set_odd_parity((des_cblock *)key); +} + +/* + * Function to convert the RID to the first decrypt key. + */ + +void sid_to_key1(uint32_t sid,unsigned char deskey[8]) +{ + unsigned char s[7]; + + s[0] = (unsigned char)(sid & 0xFF); + s[1] = (unsigned char)((sid>>8) & 0xFF); + s[2] = (unsigned char)((sid>>16) & 0xFF); + s[3] = (unsigned char)((sid>>24) & 0xFF); + s[4] = s[0]; + s[5] = s[1]; + s[6] = s[2]; + + str_to_key(s,deskey); +} + +/* + * Function to convert the RID to the second decrypt key. + */ + +void sid_to_key2(uint32_t sid,unsigned char deskey[8]) +{ + unsigned char s[7]; + + s[0] = (unsigned char)((sid>>24) & 0xFF); + s[1] = (unsigned char)(sid & 0xFF); + s[2] = (unsigned char)((sid>>8) & 0xFF); + s[3] = (unsigned char)((sid>>16) & 0xFF); + s[4] = s[0]; + s[5] = s[1]; + s[6] = s[2]; + + str_to_key(s,deskey); +} + +/* DES encrypt, for LANMAN */ + +void E1(uchar *k, uchar *d, uchar *out) +{ + des_key_schedule ks; + des_cblock deskey; + + str_to_key(k,(uchar *)deskey); +#ifdef __FreeBSD__ + des_set_key(&deskey,ks); +#else /* __FreeBsd__ */ + des_set_key((des_cblock *)deskey,ks); +#endif /* __FreeBsd__ */ + des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT); +} + + +/* Check if hive is SAM, and if it is, extract some + * global policy information from it, like lockout counts etc + */ + +void check_get_samdata(void) +{ + struct accountdb_F *f; + struct keyval *v; + + if (H_SAM >= 0) { + + /* Get users F value */ + v = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK); + if (!v) { + printf("Login counts data not found in SAM\n"); + return; + } + printf("\n* SAM policy limits:\n"); + + f = (struct accountdb_F *)&v->data; + max_sam_lock = f->locklimit; + + printf("Failed logins before lockout is: %d\n",max_sam_lock); + printf("Minimum password length : %d\n",f->minpwlen); + printf("Password history count : %d\n",f->minpwlen); + + } +} + + +/* Try to decode and possibly change account lockout etc + * This is \SAM\Domains\Account\Users\\F + * It's size seems to always be 0x50. + * Params: RID - user ID, mode - 0 silent, 1 silent, 2 edit. + * Returns: ACB bits with high bit set if lockout count is >0 + */ + +short handle_F(int rid, int mode) +{ + + struct user_F *f; + char s[200]; + struct keyval *v; + unsigned short acb; + int b; + + if (H_SAM < 0) return(0); + + /* Get users F value */ + snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid); + v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT); + if (!v) { + printf("Cannot find value <%s>\n",s); + return(0); + } + + if (v->len < 0x48) { + printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len); + FREE(v); + return(0); + } + + f = (struct user_F *)&v->data; + acb = f->ACB_bits; + + if (mode == 1) { + printf("Account bits: 0x%04x =\n",acb); + + + for (b=0; b < 15; b++) { + printf("[%s] %-15.15s | ", + (acb & (1<failedcnt,max_sam_lock); + printf("Total login count: %u\n",f->logins); + } + + if (mode == 2) { + acb |= ACB_PWNOEXP; + acb &= ~ACB_DISABLED; + acb &= ~ACB_AUTOLOCK; + f->ACB_bits = acb; + f->failedcnt = 0; + put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY,TPF_VK_EXACT); + printf("Unlocked!\n"); + } + return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15); +} + + +/* List users membership or check if admin (is in admin group) + * rid - users rid + * check - if 1 just check if admin, do not list + * returns true if user is admin + */ + +int list_user_groups(int rid, int check) +{ + char s[200]; + char g[200]; + char groupname[128]; + int nk = 0; + struct keyval *m = NULL, *c = NULL; + struct group_C *cd; + unsigned int *grps; + int count = 0, isadmin = 0; + int i, size, grp, grpnamoffs, grpnamlen; + + if (!rid || (H_SAM < 0)) return(0); + + + /* Get member list for user. Go for the first full SID, it's usually local computer I hope */ + snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\S-1-5-21-\\%08X",rid); + /* Now, the TYPE field is the number of groups the user is member of */ + /* Don't we just love the inconsistent use of fields!! */ + nk = trav_path(hive[H_SAM], 0, s, 0); + if (!nk) { + /* This probably means user is not in any group. Seems to be the case + for a couple of XPs built in support / guest users. So just return */ + if (gverbose) printf("list_user_groups(): Cannot find RID under computer SID <%s>\n",s); + return(0); + } + nk += 4; + count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT); + if (count == -1) { + printf("list_user_groups(): Cannot find value <%s\\@>\n",s); + return(0); + } + + if (!check) printf("User is member of %d groups:\n",count); + + /* This is the data size */ + size = get_val_len(hive[H_SAM],nk,"@",TPF_VK_EXACT); + + /* It should be 4 bytes for each group */ + if (gverbose) printf("Data size %d bytes.\n",size); + if (size != count * 4) { + printf("list_user_groups(): DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); + } + + m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK_EXACT); + if (!m) { + printf("list_user_groups(): Could not get value data! Giving up.\n"); + return(0); + } + + grps = (unsigned int *)&m->data; + for (i = 0; i < count; i++) { + grp = grps[i]; + if (!check) printf("%08x ",grp); + + if (grp == 0x220) isadmin = 1; + + if (!check) { + snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); + c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT); + if (c) { + cd = (struct group_C *)&c->data; + grpnamoffs = cd->grpname_ofs + 0x34; + grpnamlen = cd->grpname_len; + + cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen); + + printf("= %s (which has %d members)\n",groupname,cd->grp_members); + + } else { + printf("Group info for %x not found!\n",grp); + } + } + } + return(isadmin); +} + + + +/* Promote user into administrators group (group ID 0x220) + * And remove from all others... + * rid - users rid + * no returns yet + * THIS IS VERY HACKISH YET + */ + +void promote_user(int rid) +{ + char s[200]; + char g[200]; + int nk = 0; + struct keyval *m = NULL, *c = NULL; + struct keyval admember = { 4, 0x220 }; + unsigned int *grps, *gcnts; + int count = 0; + int i, size, grp; + + if (!rid || (H_SAM < 0)) return; + + + /* Get member list for user. Go for the first full SID, it's usually local computer I hope */ + snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\S-1-5-21-\\%08X",rid); + /* Now, the TYPE field is the number of groups the user is member of */ + /* Don't we just love the inconsistent use of fields!! */ + nk = trav_path(hive[H_SAM], 0, s, 0); + if (!nk) { + printf("Cannot find path <%s>\n",s); + return; + } + nk += 4; + count = get_val_type(hive[H_SAM],nk,"@",TPF_VK); + if (count == -1) { + printf("Cannot find value <%s\\@>\n",s); + return; + } + printf("User is member of %d groups.\n",count); + + /* This is the data size */ + size = get_val_len(hive[H_SAM],nk,"@",TPF_VK); + + /* It should be 4 bytes for each group */ + if (size != count * 4) { + printf("DEBUG: Data size %d bytes.\n",size); + printf("DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); + } + + m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK); + if (!m) { + printf("Could not get value data! Giving up.\n"); + return; + } + + printf("User was member of groups: "); + grps = (unsigned int *)&m->data; + for (i = 0; i < count; i++) { + grp = grps[i]; + printf("%08x ",grp); + switch (grp) { + case 0x220: printf("=Administrators, "); break; + case 0x221: printf("=Users, "); break; + case 0x222: printf("=Guests, "); break; + default: printf(", "); break; + } + snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); + c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK); + if (c) { + gcnts = (unsigned int *)&c->data; + gcnts[0xc]--; + /* Decrease members counter */ + put_buf2val(hive[H_SAM], c, 0, g, 0, TPF_VK); + } else { + printf("Group info for %x not found!\n",grp); + } + } +#if 1 + printf("\nDeleting user memberships\n"); + + del_value(hive[H_SAM], nk, "@", TPF_VK); + + printf("Adding into only administrators:\n"); + + if (!add_value(hive[H_SAM], nk, "@", 1)) { /* Type is # of groups, here 1 */ + printf("Failed to add @ value to key\n"); + } +#endif + put_buf2val(hive[H_SAM], &admember, nk, "@", 0, TPF_VK); + + /* Now bumb up administrator groups count */ + c = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Builtin\\Aliases\\00000220\\C", 0, TPF_VK); + if (!c) printf("Group info for 220 (adm) not found!\n"); + gcnts = (unsigned int *)&c->data; + gcnts[0xc]++; + put_buf2val(hive[H_SAM], c, 0, "\\SAM\\Domains\\Builtin\\Aliases\\00000220\\C", 0, TPF_VK); + + printf("Promotion DONE!\n"); + +} + + + + +/* Decode the V-struct, and change the password + * vofs - offset into SAM buffer, start of V struct + * rid - the users RID, required for the DES decrypt stage + * + * Some of this is ripped & modified from pwdump by Jeremy Allison + * + */ +char *change_pw(char *buf, int rid, int vlen, int stat) +{ + + uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25}; + char yn[4]; + int pl; + char *vp; + static char username[128],fullname[128]; + char comment[128],homedir[128],md4[32],lanman[32]; + char newunipw[34], newp[20], despw[20], newlanpw[16], newlandes[20]; + int username_offset,username_len; + int fullname_offset,fullname_len; + int comment_offset,comment_len; + int homedir_offset,homedir_len; + int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs,i; + int dontchange = 0; + struct user_V *v; + + des_key_schedule ks1, ks2; + des_cblock deskey1, deskey2; + + MD4_CTX context; + unsigned char digest[16]; + unsigned short acb; + + v = (struct user_V *)buf; + vp = buf; + + username_offset = v->username_ofs; + username_len = v->username_len; + fullname_offset = v->fullname_ofs; + fullname_len = v->fullname_len; + comment_offset = v->comment_ofs; + comment_len = v->comment_len; + homedir_offset = v->homedir_ofs; + homedir_len = v->homedir_len; + lmpw_offs = v->lmpw_ofs; + lmpw_len = v->lmpw_len; + ntpw_offs = v->ntpw_ofs; + ntpw_len = v->ntpw_len; + + if (!rid) { + printf("No RID given. Unable to change passwords..\n"); + return(0); + } + + if (gverbose) { + printf("lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len); + printf("ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len); + } + + *username = 0; + *fullname = 0; + *comment = 0; + *homedir = 0; + + if(username_len <= 0 || username_len > vlen || + username_offset <= 0 || username_offset >= vlen || + comment_len < 0 || comment_len > vlen || + fullname_len < 0 || fullname_len > vlen || + homedir_offset < 0 || homedir_offset >= vlen || + comment_offset < 0 || comment_offset >= vlen || + lmpw_offs < 0 || lmpw_offs >= vlen) + { + if (stat != 1) printf("change_pw: Not a legal V struct? (negative struct lengths)\n"); + return(NULL); + } + + /* Offsets in top of struct is relative to end of pointers, adjust */ + username_offset += 0xCC; + fullname_offset += 0xCC; + comment_offset += 0xCC; + homedir_offset += 0xCC; + ntpw_offs += 0xCC; + lmpw_offs += 0xCC; + + cheap_uni2ascii(vp + username_offset,username,username_len); + cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len); + cheap_uni2ascii(vp + comment_offset,comment,comment_len); + cheap_uni2ascii(vp + homedir_offset,homedir,homedir_len); + +#if 0 + /* Reset hash-lengths to 16 if syskey has been reset */ + if (syskeyreset && ntpw_len > 16 && !stat) { + ntpw_len = 16; + lmpw_len = 16; + ntpw_offs -= 4; + (unsigned int)*(vp+0xa8) = ntpw_offs - 0xcc; + *(vp + 0xa0) = 16; + *(vp + 0xac) = 16; + } +#endif + + if (stat) { + acb = handle_F(rid,0); + printf("| %04x | %-30.30s | %-6s | %-8s |\n", + rid, username, (list_user_groups(rid,1) ? "ADMIN" : "") , ( acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "") ); + return(username); + } + + printf("\nRID : %04d [%04x]\n",rid,rid); + printf("Username: %s\n",username); + printf("fullname: %s\n",fullname); + printf("comment : %s\n",comment); + printf("homedir : %s\n\n",homedir); + + list_user_groups(rid,0); + printf("\n"); + + acb = handle_F(rid,1); + + if (lmpw_len < 16 && gverbose) { + printf("** LANMAN password not set. User MAY have a blank password.\n** Usually safe to continue. Normal in Vista\n"); + } + + if (ntpw_len < 16) { + printf("** No NT MD4 hash found. This user probably has a BLANK password!\n"); + if (lmpw_len < 16) { + printf("** No LANMAN hash found either. Sorry, cannot change. Try login with no password!\n"); + dontchange = 1; + } else { + printf("** LANMAN password IS however set. Will now install new password as NT pass instead.\n"); + printf("** NOTE: Continue at own risk!\n"); + ntpw_offs = lmpw_offs; + *(vp+0xa8) = ntpw_offs - 0xcc; + ntpw_len = 16; + lmpw_len = 0; + } + } + + if (gverbose) { + hexprnt("Crypted NT pw: ",(unsigned char *)(vp+ntpw_offs),16); + hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16); + } + + /* Get the two decrpt keys. */ + sid_to_key1(rid,(unsigned char *)deskey1); + des_set_key((des_cblock *)deskey1,ks1); + sid_to_key2(rid,(unsigned char *)deskey2); + des_set_key((des_cblock *)deskey2,ks2); + + /* Decrypt the NT md4 password hash as two 8 byte blocks. */ + des_ecb_encrypt((des_cblock *)(vp+ntpw_offs ), + (des_cblock *)md4, ks1, DES_DECRYPT); + des_ecb_encrypt((des_cblock *)(vp+ntpw_offs + 8), + (des_cblock *)&md4[8], ks2, DES_DECRYPT); + + /* Decrypt the lanman password hash as two 8 byte blocks. */ + des_ecb_encrypt((des_cblock *)(vp+lmpw_offs), + (des_cblock *)lanman, ks1, DES_DECRYPT); + des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8), + (des_cblock *)&lanman[8], ks2, DES_DECRYPT); + + if (gverbose) { + hexprnt("MD4 hash : ",(unsigned char *)md4,16); + hexprnt("LANMAN hash : ",(unsigned char *)lanman,16); + } + + printf("\n- - - - User Edit Menu:\n"); + printf(" 1 - Clear (blank) user password\n" + " 2 - Edit (set new) user password (careful with this on XP or Vista)\n" + " 3 - Promote user (make user an administrator)\n"); + printf("%s4 - Unlock and enable user account%s\n", (acb & 0x8000) ? " " : "(", + (acb & 0x8000) ? " [probably locked now]" : ") [seems unlocked already]"); + printf(" q - Quit editing user, back to user select\n"); + + pl = fmyinput("Select: [q] > ",newp,16); + + if ( (pl < 1) || (*newp == 'q') || (*newp == 'Q')) return(0); + + if (*newp == '3') { + printf("NOTE: This function is still experimental, and in some cases it\n" + " may result in stangeness when editing user/group in windows.\n" + " Also, users (like Guest often is) may still be prevented\n" + " from login via security/group policies which is not changed.\n"); + fmyinput("Do you still want to promote the user? (y/n) [n] ",yn,2); + if (*yn == 'y' || *yn == 'Y') { + promote_user(rid); + } + return(username); + } + + if (*newp == '4') { + acb = handle_F(rid,2); + return(username); + } + + if (*newp == '2') { + + if (dontchange) { + printf("Sorry, unable to edit since password seems blank already (thus no space for it)\n"); + return(0); + } + + pl = fmyinput("New Password: ",newp,16); + + if (pl < 1) { + printf("No change.\n"); + return(0); + } + + cheap_ascii2uni(newp,newunipw,pl); + + make_lanmpw(newp,newlanpw,pl); + + /* printf("Ucase Lanman: %s\n",newlanpw); */ + + MD4Init (&context); + MD4Update (&context, newunipw, pl<<1); + MD4Final (digest, &context); + + if (gverbose) hexprnt("\nNEW MD4 hash : ",digest,16); + + E1((uchar *)newlanpw, x1, (uchar *)lanman); + E1((uchar *)newlanpw+7, x1, (uchar *)lanman+8); + + if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16); + + /* Encrypt the NT md4 password hash as two 8 byte blocks. */ + des_ecb_encrypt((des_cblock *)digest, + (des_cblock *)despw, ks1, DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)(digest+8), + (des_cblock *)&despw[8], ks2, DES_ENCRYPT); + + des_ecb_encrypt((des_cblock *)lanman, + (des_cblock *)newlandes, ks1, DES_ENCRYPT); + des_ecb_encrypt((des_cblock *)(lanman+8), + (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT); + + if (gverbose) { + hexprnt("NEW DES crypt : ",(unsigned char *)despw,16); + hexprnt("NEW LANMAN crypt: ",(unsigned char *)newlandes,16); + } + + /* Reset hash length to 16 if syskey enabled, this will cause + * a conversion to syskey-hashes upon next boot */ + if (syskeyreset && ntpw_len > 16) { + ntpw_len = 16; + lmpw_len = 16; + ntpw_offs -= 4; + *(vp+0xa8) = (unsigned int)(ntpw_offs - 0xcc); + *(vp + 0xa0) = 16; + *(vp + 0xac) = 16; + } + + for (i = 0; i < 16; i++) { + *(vp+ntpw_offs+i) = (unsigned char)despw[i]; + if (lmpw_len >= 16) *(vp+lmpw_offs+i) = (unsigned char)newlandes[i]; + } + + printf("Password changed!\n"); + + + } /* new password */ + + else if (pl == 1 && *newp == '1') { + /* Setting hash lengths to zero seems to make NT think it is blank + * However, since we cant cut the previous hash bytes out of the V value + * due to missing resize-support of values, it may leak about 40 bytes + * each time we do this. + */ + v->ntpw_len = 0; + v->lmpw_len = 0; + + printf("Password cleared!\n"); + } + +#if 0 + hexprnt("Pw in buffer: ",(vp+ntpw_offs),16); + hexprnt("Lm in buffer: ",(vp+lmpw_offs),16); +#endif + dirty = 1; + return(username); +} + + +/* Registry edit wrapper */ + +void mainloop(void) +{ + regedit_interactive(hive, no_hives); +} + + +/* List users in SAM file + * pageit - hmm.. forgot this one for this release.. + */ + +int list_users(int pageit) +{ + char s[200]; + struct keyval *v; + int nkofs /* ,vkofs */ ; + int rid; + int count = 0, countri = 0; + struct ex_data ex; + + if (H_SAM < 0) return(1); + nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Account\\Users\\Names\\",0); + if (!nkofs) { + printf("list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n"); + return(1); + } + + printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n"); + + while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) { + + /* 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|TPF_VK_SHORT); + if (rid == 500) strncpy(admuser,ex.name,128); /* Copy out admin-name */ + + /* 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); + if (!v) { + printf("Cannot find value <%s>\n",s); + return(1); + } + + if (v->len < 0xcc) { + printf("list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", + s, v->len); + } else { + change_pw( (char *)&v->data , rid, v->len, 1); + } + FREE(v); + FREE(ex.name); + } + return(0); +} + + +/* Find a username in the SAM registry, then get it's V-value, + * and feed it to the password changer. + */ + +void find_n_change(char *username) +{ + char s[200]; + struct keyval *v; + int rid = 0; + + if ((H_SAM < 0) || (!username)) return; + if (*username == '0' && *(username+1) == 'x') sscanf(username,"%i",&rid); + + if (!rid) { /* Look up username */ + /* Extract the unnamed value out of the username-key, value is RID */ + snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",username); + 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; + } + } + + /* + printf("Username: %s, RID = %d (0x%0x)\n",username,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); + if (!v) { + printf("Cannot find value <%s>\n",s); + return; + } + + if (v->len < 0xcc) { + printf("Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n", + s, v->len); + } else { + change_pw( (char *)&v->data , rid, v->len, 0); + if (dirty) { + if (!(put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY, TPF_VK_EXACT))) { + printf("Failed to write updated <%s> to registry! Password change not completed!\n",s); + } + } + } + FREE(v); +} + +/* Check for presence of syskey and possibly disable it if + * user wants it. + * This is tricky, and extremely undocumented! + * See docs for more info on what's going on when syskey is installed + */ + +#undef LSADATA + +void handle_syskey(void) +{ + + /* This is \SAM\Domains\Account\F */ + struct samkeyf { + char unknown[0x50]; /* 0x0000 - Unknown. May be machine SID */ + char unknown2[0x14]; + char syskeymode; /* 0x0064 - Type/mode of syskey in use */ + char syskeyflags1[0xb]; /* 0x0065 - More flags/settings */ + char syskeyobf[0x30]; /* 0x0070 - This may very well be the obfuscated syskey */ + }; /* There may be more, usually 8 null-bytes? */ + + /* Security\Policy\SecretEncryptionKey\@, only on NT5 */ + /* Probably contains some keyinfo for syskey. Second DWORD seems to be syskeymode */ + struct secpoldata { + int unknown1; /* Some kind of flag? usually 1 */ + int syskeymode; /* Is this what we're looking for? */ + int unknown2; /* Usually 0? */ + char keydata[0x40]; /* Some kind of scrambled keydata? */ + }; + +#ifdef LSADATA + /* SYSTEM\CurrentControlSet\Control\Lsa\Data, only on NT5?? */ + /* Probably contains some keyinfo for syskey. Byte 0x34 seems to be mode */ + struct lsadata { + char keydata[0x34]; /* Key information */ + int syskeymode; /* Is this what we're looking for? */ + }; +#endif + + /* void *fdata; */ + struct samkeyf *ff = NULL; + struct secpoldata *sf = NULL; + /* struct lsadata *ld = NULL; */ + int /* len, */ i,secboot, samfmode, secmode /* , ldmode */ ; + struct keyval *samf, *secpol /* , *lsad */ ; + char *syskeytypes[4] = { "off", "key-in-registry", "enter-passphrase", "key-on-floppy" }; + char yn[5]; + + printf("\n---------------------> SYSKEY CHECK <-----------------------\n"); + + + if (H_SAM < 0) { + printf("ERROR: SAM hive not loaded!\n"); + return; + } + samf = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); + + if (samf && samf->len > 0x70 ) { + ff = (struct samkeyf *)&samf->data; + samfmode = ff->syskeymode; + } else { + samfmode = -1; + } + + secboot = -1; + if (H_SYS >= 0) { + secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT ); + } + + secmode = -1; + if (H_SEC >=0) { + secpol = get_val2buf(hive[H_SEC], NULL, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_NONE, TPF_VK_EXACT); + if (secpol) { /* Will not be found in NT 4, take care of that */ + sf = (struct secpoldata *)&secpol->data; + secmode = sf->syskeymode; + } + } + +#ifdef LSADATA + lsad = get_val2buf(hive[H_SYS], NULL, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); + + if (lsad && lsad->len >= 0x38) { + ld = (struct lsadata *)&lsad->data; + ldmode = ld->syskeymode; + } else { + ldmode = -1; + } +#endif + + printf("SYSTEM SecureBoot : %d -> %s\n", secboot, + (secboot < 0 || secboot > 3) ? "Not Set (not installed, good!)" : syskeytypes[secboot]); + printf("SAM Account\\F : %d -> %s\n", samfmode, + (samfmode < 0 || samfmode > 3) ? "Not Set" : syskeytypes[samfmode]); + printf("SECURITY PolSecretEncryptionKey: %d -> %s\n", secmode, + (secmode < 0 || secmode > 3) ? "Not Set (OK if this is NT4)" : syskeytypes[secmode]); + +#ifdef LSADATA + printf("SYSTEM LsaData : %d -> %s\n\n", ldmode, + (ldmode < 0 || ldmode > 3) ? "Not Set (strange?)" : syskeytypes[ldmode]); +#endif + + if (secboot != samfmode && secboot != -1) { + printf("WARNING: Mismatch in syskey settings in SAM and SYSTEM!\n"); + printf("WARNING: It may be dangerous to continue (however, resetting syskey\n"); + printf(" may very well fix the problem)\n"); + } + + if (secboot > 0 || samfmode > 0) { + printf("\n***************** SYSKEY IS ENABLED! **************\n"); + printf("This installation very likely has the syskey passwordhash-obfuscator installed\n"); + printf("It's currently in mode = %d, %s-mode\n",secboot, + (secboot < 0 || secboot > 3) ? "Unknown" : syskeytypes[secboot]); + + if (no_hives < 2) { + printf("\nSYSTEM (and possibly SECURITY) hives not loaded, unable to disable syskey!\n"); + printf("Please start the program with at least SAM & SYSTEM-hive filenames as arguments!\n\n"); + return; + } + printf("SYSKEY is on! However, DO NOT DISABLE IT UNLESS YOU HAVE TO!\n"); + printf("This program can change passwords even if syskey is on, however\n"); + printf("if you have lost the key-floppy or passphrase you can turn it off,\n"); + printf("but please read the docs first!!!\n"); + 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("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') { + /* Reset SAM syskey infostruct, fill with zeroes */ + if (ff) { + ff->syskeymode = 0; + + for (i = 0; i < 0x3b; i++) { + ff->syskeyflags1[i] = 0; + } + + put_buf2val(hive[H_SAM], samf, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT); + + } + /* Reset SECURITY infostruct (if any) */ + if (sf) { + memset(sf, 0, secpol->len); + sf->syskeymode = 0; + + put_buf2val(hive[H_SEC], secpol, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_BINARY, TPF_VK_EXACT); + + } + +#if LSADATA + if (ld) { + + ld->syskeymode = 0; + + put_buf2val(hive[H_SYS], lsad, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT); + + } +#endif + + /* And SYSTEM SecureBoot parameter */ + + put_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT, 0); + + dirty = 1; + syskeyreset = 1; + printf("Updating passwordhash-lengths..\n"); + list_users(1); + printf("* SYSKEY RESET!\nNow please set new administrator password!\n"); + } else { + + syskeyreset = 1; + } + } else { + printf("Syskey not installed!\n"); + return; + } + +} + + +/* Interactive user edit */ +void useredit(void) +{ + char iwho[100]; + int il; + + printf("\n\n===== chntpw Edit User Info & Passwords ====\n\n"); + + if (H_SAM < 0) { + printf("ERROR: SAM registry file (which contains user data) is not loaded!\n\n"); + return; + } + + + list_users(1); + + while (1) { + printf("\nSelect: ! - quit, . - list users, 0x - User with RID (hex)\n"); + printf("or simply enter the username to change: [%s] ",admuser); + il = fmyinput("",iwho,32); + if (il == 1 && *iwho == '.') { printf("\n"); list_users(1); continue; } + if (il == 1 && *iwho == '!') return; + if (il == 0) strcpy(iwho,admuser); + find_n_change(iwho); + } + +} + + +void recoveryconsole() +{ + + int cmd = 0; + int sec = 0; + static char *scpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SetCommand"; + static char *slpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel"; + char yn[5]; + + if (H_SOF < 0) { + printf("\nSOFTWARE-hive not loaded, and there's where RecoveryConsole settings are..\n"); + return; + } + + cmd = get_dword(hive[H_SOF],0,scpath,TPF_VK_EXACT); + 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)\n"); + return; + } + + printf("\nRecoveryConsole:\n- Extended SET command is \t%s\n", cmd>0 ? "ENABLED (1)" : "DISABLED (0)"); + printf("- Administrator password login: %s\n", sec>0 ? "SKIPPED (1)" : "ENFORCED (0)"); + + fmyinput("\nDo you want to change it? (y/n) [n] ",yn,2); + if (*yn == 'y') { + cmd ^= 1; + sec ^= 1; + if (!put_dword(hive[0], 0, scpath, TPF_VK_EXACT, cmd)) printf("Update of SET level failed registry edit\n"); + if (!put_dword(hive[0], 0, slpath, TPF_VK_EXACT, sec)) printf("Update of login level failed registry edit\n"); + printf("Done!\n"); + } + +} + + +/* Interactive menu system */ + +void interactive(void) +{ + int il; + char inbuf[20]; + + while(1) { + printf("\n\n<>========<> chntpw Main Interactive Menu <>========<>\n\n" + "Loaded hives:"); + for (il = 0; il < no_hives; il++) { + printf(" <%s>",hive[il]->filename); + } + + 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"); + + il = fmyinput("What to do? [1] -> ", inbuf, 10); + + if (!il) useredit(); + if (il) { + switch(inbuf[0]) { + 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; + } + } + } +} + + + +void usage(void) { + 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" + " -l list all users in SAM file\n" + " -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" + " -v Be a little more verbose (for debuging)\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" + ); +} + +int main(int argc, char **argv) +{ + + int dodebug = 0, list = 2, inter = 0,edit = 0,il,d = 0, dd = 0, logchange = 0, mode = 0; + extern int /* opterr, */ optind; + extern char* optarg; + char *filename,c; + char *who = "Administrator"; + char iwho[100]; + FILE *ch; /* Write out names of touched files to this */ + + char *options = "LENidehlvu:"; + + printf("%s\n",chntpw_version); + while((c=getopt(argc,argv,options)) > 0) { + switch(c) { + case 'd': dodebug = 1; break; + 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 'v': mode |= HMODE_VERBOSE; gverbose = 1; break; + case 'i': list = 2; who = 0; inter = 1; break; + case 'u': who = optarg; list = 2; break; + case 'h': usage(); exit(0); break; + default: usage(); exit(1); break; + } + } + filename=argv[optind]; + if (!filename || !*filename) { + usage(); exit(1); + } + do { + if (!(hive[no_hives] = openHive(filename, + HMODE_RW|mode))) { + printf("Unable to open/read a hive, exiting..\n"); + exit(1); + } + switch(hive[no_hives]->type) { + case HTYPE_SAM: H_SAM = no_hives; break; + case HTYPE_SOFTWARE: H_SOF = no_hives; break; + case HTYPE_SYSTEM: H_SYS = no_hives; break; + case HTYPE_SECURITY: H_SEC = no_hives; break; + } + no_hives++; + filename = argv[optind+no_hives]; + } while (filename && *filename && no_hives < MAX_HIVES); + + if (dodebug) debugit(hive[0]->buffer,hive[0]->size); + else { + + check_get_samdata(); + if (list && !edit && !inter) { + if ( list_users(1) ) edit = 1; + } + if (edit) mainloop(); + else if (who) { handle_syskey(); find_n_change(who); } + + if (inter) interactive(); + } + + if (list != 1) { + 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>",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; + } + } + 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("Write hive files? (y/n) [n] : ",iwho,3); + if (*iwho == 'y' || logchange) { + if (logchange) { + ch = fopen("/tmp/changed","w"); + } + for (il = 0; il < no_hives; il++) { + if (hive[il]->state & HMODE_DIRTY) { + printf("%2d <%s> - ",il,hive[il]->filename); + if (!writeHive(hive[il])) { + 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; + } + } + } + if (logchange) { + fprintf(ch,"\n"); + fclose(ch); + } + } else { + printf("Not written!\n\n"); + } + } else { + printf("None!\n\n"); + } + } /* list only check */ + return(dd); +} diff --git a/cpnt.c b/cpnt.c new file mode 100644 index 0000000..38c0e19 --- /dev/null +++ b/cpnt.c @@ -0,0 +1,100 @@ +/* + * cpnt.c - Copy over file without truncating. + * For use on my current floppy, since it's 'cp' insist + * on truncating first, and NTFS doesn't like that yet. + * + * 2003-apr: First version + * + ***** + * + * Copyright (c) 1997-2007 Petter Nordahl-Hagen. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See file GPL.txt for the full license. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define BUFSIZE 16384 + +int main(int argc, char **argv) +{ + + void *buf; + int sf,df,rb,wb; + int going = 1; + int e = 0; + + if (argc != 3) { + printf("usage: cpnt \n"); + printf(" sorry, only one file at a time yet.\n"); + return(1); + } + +#if 0 + printf("input : %s\n",argv[1]); + printf("output: %s\n",argv[2]); +#endif + + buf = malloc(BUFSIZE); + if (!buf) { + printf("cpnt: could not allocate buffer\n"); + return(1); + } + + sf = open(argv[1],O_RDONLY); + if (sf < 0) { + e = errno; + printf("cpnt: %s: %s\n",argv[1],strerror(e)); + return(1); + } + + df = open(argv[2],O_WRONLY|O_CREAT,00666); + if (df < 0) { + e = errno; + printf("cpnt: %s: %s\n",argv[2],strerror(e)); + return(1); + } + + while (going) { + rb = read(sf,buf,BUFSIZE); + if (rb < 0) { + e = errno; + printf("cpnt: error while reading: %s\n",strerror(e)); + going = 0; + break; + } + if (rb == 0) going = 0; + wb = write(df,buf,rb); + if (wb < 0) { + e = errno; + printf("cpnt: error while writing: %s\n",strerror(e)); + going = 0; + } + + } + + close(sf); + close(df); + free(buf); + + return(e ? 1 : 0); +} diff --git a/edlib.c b/edlib.c new file mode 100644 index 0000000..7ef4318 --- /dev/null +++ b/edlib.c @@ -0,0 +1,691 @@ +/* + * edlib.c - Registry edit interactive fuctions. + * + * 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-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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See file GPL.txt for the full license. + * + ***** + */ + + +#include +#include +#include +#include +#include + +#include "ntreg.h" + +const char edlib_version[] = "edlib version 0.1 110511, (c) Petter N Hagen"; + +#define ALLOC_DEBUG 0 /* Reg allocation debug hooks */ +#define ADDBIN_DEBUG 0 /* Reg expansion debug hooks */ + +extern char *val_types[REG_MAX+1]; + +struct cmds { + char cmd_str[12]; + int cmd_num; +}; + +#define MCMD_CD 1 +#define MCMD_LS 2 +#define MCMD_QUIT 3 +#define MCMD_CAT 4 +#define MCMD_STRUCT 5 +#define MCMD_DEBUG 6 +#define MCMD_HELP 7 +#define MCMD_EXPORTKEY 8 +#define MCMD_HIVE 9 +#define MCMD_EDIT 10 +#define MCMD_ALLOC 11 +#define MCMD_FREE 12 +#define MCMD_ADDV 13 +#define MCMD_DELV 14 +#define MCMD_DELVALL 15 +#define MCMD_NEWKEY 16 +#define MCMD_DELKEY 17 +#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 } , + { "ls" , MCMD_LS } , + { "dir", MCMD_LS } , + { "q" , MCMD_QUIT } , + { "cat", MCMD_CAT } , + { "type",MCMD_CAT } , + { "st" , MCMD_STRUCT } , + { "debug", MCMD_DEBUG } , + { "hive", MCMD_HIVE } , + { "ed", MCMD_EDIT } , +#if ALLOC_DEBUG + { "alloc", MCMD_ALLOC } , + { "free", MCMD_FREE } , +#endif +#if ADDBIN_DEBUG + { "addbin", MCMD_ADDBIN }, +#endif + { "nv", MCMD_ADDV } , + { "dv", MCMD_DELV } , + { "delallv", MCMD_DELVALL } , + { "nk", MCMD_NEWKEY } , + { "dk", MCMD_DELKEY } , + { "hex", MCMD_CATHEX } , + { "rdel", MCMD_RDEL } , + { "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 + * Does not handle all types yet (does a hexdump instead) + */ +void cat_vk(struct hive *hdesc, int nkofs, char *path, int dohex) +{ + void *data; + int len,i,type; + // char string[SZ_MAX+1]; + char *string = NULL; + struct keyval *kv = NULL; + + 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, TPF_VK); + if (!len) { + printf("cat_vk: Value <%s> has zero length\n",path); + return; + } + +#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); + + if (dohex) type = REG_BINARY; + switch (type) { + case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + 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); + break; + default: + printf("Don't know how to handle type yet!\n"); + case REG_BINARY: + hexdump((char *)data, 0, len, 1); + } + putchar('\n'); + FREE(kv); + +} + +/* Edit value: Invoke whatever is needed to edit it + * based on its type + */ + +void edit_val(struct hive *h, int nkofs, char *path) +{ + struct keyval *kv, *newkv; + int type,len,n,i,in,go, newsize, d = 0, done, insert = 0; + char inbuf[SZ_MAX+4]; + char origstring[SZ_MAX+4]; + char *newstring; + char *dbuf; + + type = get_val_type(h, nkofs, path, TPF_VK); + if (type == -1) { + printf("Value <%s> not found!\n",path); + return; + } + + kv = get_val2buf(h, NULL, nkofs, path, type, TPF_VK); + if (!kv) { + printf("Unable to get data of value <%s>\n",path); + return; + } + len = kv->len; + + printf("EDIT: <%s> of type %s with length %d [0x%x]\n", path, + (type < REG_MAX ? val_types[type] : "(unknown)"), + len, len); + + switch(type) { + case REG_DWORD: + printf("DWORD: Old value %d [0x%x], ", kv->data, kv->data); + fmyinput("enter new value (prepend 0x if hex, empty to keep old value)\n-> ", + inbuf, 12); + if (*inbuf) { + sscanf(inbuf,"%i",&kv->data); + d = 1; + } + printf("DWORD: New value %d [0x%x], ", kv->data, kv->data); + break; + case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + newstring = NULL; + dbuf = (char *)&kv->data; + cheap_uni2ascii(dbuf,origstring,len); + n = 0; i = 0; + while (i < (len>>1)-1) { + printf("[%2d]: %s\n",n,origstring+i); + i += strlen(origstring+i) + 1; + n++; + } + + printf("\nNow enter new strings, one by one.\n"); + printf("Enter nothing to keep old.\n"); + if (type == REG_MULTI_SZ) { + printf("'--n' to quit (remove rest of strings)\n"); + printf("'--i' insert new string at this point\n"); + printf("'--q' to quit (leaving remaining strings as is)\n"); + printf("'--Q' to quit and discard all changes\n"); + printf("'--e' for empty string in this position\n"); + } + n = 0; i = 0; in = 0; go = 0; done = 0; + + /* Now this one is RATHER UGLY :-} */ + + while (i < (len>>1)-1 || !done) { + + printf("[%2d]: %s\n",n, insert == 1 ? "[INSERT]" : ((i < (len>>1)-1 ) ? origstring+i : "[NEW]")); + if (insert) insert++; + if (!go) fmyinput("-> ",inbuf, 500); + else *inbuf = 0; + if (*inbuf && strcmp("--q", inbuf)) { + if (!strcmp("--n", inbuf) || !strcmp("--Q", inbuf)) { /* Zap rest */ + i = (len>>1) ; done = 1; + } else if (strcmp("--i", inbuf)) { /* Copy out given string */ + if (!strcmp("--e",inbuf)) *inbuf = '\0'; + if (newstring) newstring = realloc(newstring, in+strlen(inbuf)+1); + else newstring = malloc(in+strlen(inbuf)+1); + strcpy(newstring+in, inbuf); + in += strlen(inbuf)+1; + } else { + insert = 1; + } + } else { /* Copy out default string */ + + if (newstring) newstring = realloc(newstring, in+strlen(origstring+i)+1); + else newstring = malloc(in + strlen(origstring+i) + 1); + strcpy(newstring+in, origstring+i); + in += strlen(origstring+i)+1; + + if (!strcmp("--q", inbuf)) { + go = 1; done = 1; + if (!(i < (len>>1)-1 )) { + in--; /* remove last empty if in NEW-mode */ + } + } + } + + if (!insert) i += strlen(origstring+i) + 1; + if (insert != 1) n++; + if (insert == 2) insert = 0; + if (type != REG_MULTI_SZ) { + i = (len<<1); + done = 1; + } + + } + + if (strcmp("--Q", inbuf)) { /* We didn't bail out */ + if (newstring) newstring = realloc(newstring, in+1); + else newstring = malloc(in+1); + if (type == REG_MULTI_SZ) { + in++; + *(newstring+in) = '\0'; /* Must add null termination */ + } + ALLOC(newkv,1,(in<<1)+sizeof(int)); + newkv->len = in<<1; + printf("newkv->len: %d\n",newkv->len); + cheap_ascii2uni(newstring, (char *)&(newkv->data), in); + + d = 1; + + FREE(kv); + kv = newkv; + + } + break; + + default: + printf("Type not handeled (yet), invoking hex editor on data!\n"); + case REG_BINARY: + fmyinput("New length (ENTER to keep same): ",inbuf,90); + if (*inbuf) { + 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)); + FREE(kv); + kv = newkv; + kv->len = newsize; + } + d = debugit((char *)&kv->data, kv->len); + break; + } + + if (d) { + if (!(put_buf2val(h, kv, nkofs, path, type, TPF_VK))) { + printf("Failed to set value!?\n"); + } + } + FREE(kv); +} + +/* look up command in array + */ +int parsecmd(char **s, struct cmds *cmd) +{ + + int l = 0; + + while ((*s)[l] && ((*s)[l] != ' ')) { + l++; + } + while (cmd->cmd_num) { + if (!strncmp(*s, cmd->cmd_str, l)) { + *s += l; + return(cmd->cmd_num); + } + cmd++; + } + return(0); +} + + +/* Lot of people didn't understand the "nv" command. + * Actually the command should understand the type names too, but.. some later time + */ + +void nv_help(void) +{ + int i; + + printf("Command syntax is:\n\n" + " nv \n\n" + "where should be the HEX NUMBER from one of these registry value types:\n\n"); + + for (i=0; i < REG_MAX; i++) { + printf(" %2x : %s\n",i,val_types[i]); + } + printf("\nExample:\n nv 4 foobar\n"); + printf("to make a new value named foobar of the type REG_DWORD\n\n"); +} + + +/* Interactive registry editor + * hive - list of loaded hives (array pointing to hive structs) + * no_hives - max number of hives loaded + */ + +void regedit_interactive(struct hive *hive[], int no_hives) +{ + struct hive *hdesc; + int cdofs, newofs; + struct nk_key *cdkey; + char inbuf[100], *bp, *file, *prefix; + char path[1000]; + int l, vkofs, nh, i; + int usehive = 0; + struct keyval *kv; + +#if ALLOC_DEBUG + int pagestart; + int freetest; +#endif + + hdesc = hive[usehive]; + cdofs = hdesc->rootofs; + + printf("Simple registry editor. ? for help.\n"); + + while (1) { + cdkey = (struct nk_key *)(hdesc->buffer + cdofs); + + *path = 0; + get_abs_path(hdesc,cdofs+4, path, 50); + +#if ALLOC_DEBUG + pagestart = find_page_start(hdesc,cdofs); + printf("find_page_start: 0x%x\n",pagestart); + freetest = find_free_blk(hdesc,pagestart,10); + printf("find_free_blk: 0x%x\n",freetest); +#endif + if (hdesc->state & HMODE_VERBOSE) printf("\n[%0x] %s> ",cdofs,path); + else printf("\n%s> ",path); + l = fmyinput("",inbuf,90); + bp = inbuf; + skipspace(&bp); + + if (l > 0 && *bp) { + switch(parsecmd(&bp,maincmds)) { + case MCMD_HELP: + printf("Simple registry editor:\n"); + printf("hive [] - list loaded hives or switch to hive numer n\n"); + 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"); + printf("dk - delete key (must be empty)\n"); + printf("ed - Edit value\n"); + printf("nv - Add value\n"); + printf("dv - Delete value\n"); + printf("delallv - Delete all values in current key\n"); + printf("rdel - Recursively delete key & subkeys\n"); + printf("ek - export key to (Windows .reg file format)\n"); + printf("debug - enter buffer hexeditor\n"); + printf("st [] - debug function: show struct info\n"); + printf("q - quit\n"); + break; + + case MCMD_DELKEY : + bp++; + skipspace(&bp); + del_key(hdesc, cdofs + 4, bp); + break; + case MCMD_NEWKEY : + bp++; + skipspace(&bp); + add_key(hdesc, cdofs + 4, bp); + break; + case MCMD_DELVALL : + bp++; + skipspace(&bp); + del_allvalues(hdesc, cdofs + 4); + break; + case MCMD_DELV : + bp++; + skipspace(&bp); + del_value(hdesc, cdofs + 4, bp, 0); + break; + case MCMD_ADDV : + bp++; + skipspace(&bp); + if (!isxdigit(*bp)) { + nv_help(); + break; + } + nh = gethex(&bp); + skipspace(&bp); + if (!*bp) { + nv_help(); + break; + } + add_value(hdesc, cdofs+4, bp, nh); + break; +#if ALLOC_DEBUG + case MCMD_FREE : + bp++; + skipspace(&bp); + nh = gethex(&bp); + free_block(hdesc, nh); + break; + case MCMD_ALLOC : + bp++; + skipspace(&bp); + nh = gethex(&bp); + 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); + nk_ls(hdesc, bp, cdofs+4, 0); + break; + case MCMD_CK : + bp++; + skipspace(&bp); + kv = get_class(hdesc, cdofs+4, bp); + if (kv) { + hexdump((char *)&kv->data, 0, kv->len, 1); + FREE(kv); + } + break; + case MCMD_RDEL : + bp++; + skipspace(&bp); + rdel_keys(hdesc, bp, cdofs+4); + break; + case MCMD_EDIT : + bp++; + skipspace(&bp); + edit_val(hdesc, cdofs+4, bp); + break; + case MCMD_HIVE : + bp++; + skipspace(&bp); + if (*bp) { + nh = gethex(&bp); + if (nh >= 0 && nh < no_hives) { + usehive = nh; + printf("Switching to hive #%d, named <%s>, size %d [0x%x]\n", + usehive, hive[usehive]->filename, + hive[usehive]->size, + hive[usehive]->size); + hdesc = hive[usehive]; + cdofs = hdesc->rootofs; + } + } else { + for (nh = 0; nh < no_hives; nh++) { + printf("%c %c %2d %9d 0x%08x <%s>\n", (nh == usehive) ? '*' : ' ', + (hive[nh]->state & HMODE_DIRTY) ? 'D' : ' ', + nh, + hive[nh]->size, + hive[nh]->size, hive[nh]->filename); + } + } + break; + case MCMD_CD : + bp++; + skipspace(&bp); + newofs = trav_path(hdesc, cdofs+4,bp,TPF_NK); + if (newofs) cdofs = newofs; + else printf("Key %s not found!\n",bp); + break; + case MCMD_CAT: + bp++; + 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); + cat_vk(hdesc,cdofs+4,bp,1); + break; + case MCMD_EXPORTKEY : + bp++; + skipspace(&bp); + file = bp; + i = 0; + while(*bp != ' ' && (*bp)) + { + i++; + bp++; + } + file[i] = '\0'; + bp++; + skipspace(&bp); + prefix = bp; + i = 0; + while(*bp != ' ' && (*bp)) + { + i++; + bp++; + } + prefix[i] = '\0'; + bp++; + skipspace(&bp); + export_key(hdesc, cdofs + 4, bp, file, prefix); + break; + case MCMD_STRUCT: + bp++; + skipspace(&bp); + vkofs = cdofs; + if (*bp) { + vkofs = gethex(&bp); + } + parse_block(hdesc,vkofs,2); + break; + case MCMD_DEBUG: + if (debugit(hdesc->buffer,hdesc->size)) hdesc->state |= HMODE_DIRTY; + break; + case MCMD_QUIT: + return; + break; + default: + printf("Unknown command: %s, type ? for help\n",bp); + break; + } + } + } +} diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..918a819 --- /dev/null +++ b/hash.c @@ -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 --git a/ntreg.c b/ntreg.c new file mode 100644 index 0000000..dd70c57 --- /dev/null +++ b/ntreg.c @@ -0,0 +1,4239 @@ +/* + * 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 + * 2008-mar: Fixed bug which skipped first indirect index table when deleting keys, + * usually leading to endless loop when recursive deleting. + * 2008-mar: Export to .reg file by Leo von Klenze, expanded a bit by me. + * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev + * http://bugs.gentoo.org/show_bug.cgi?id=185411 + * 2007-sep: Verbosity/debug messages minor changes + * 2007-apr: LGPL license. + * 2004-aug: Deep indirect index support. NT351 support. Recursive delete. + * Debugged a lot in allocation routines. Still no expansion. + * 2004-jan: Verbosity updates + * 2003-jan: Allocation of new data, supports adding/deleting keys & stuff. + * Missing is expanding the file. + * 2003-jan: Seems there may be garbage pages at end of file, not zero pages + * now stops enumerating at first non 'hbin' page. + * + * NOTE: The API is not frozen. It can and will change every release. + * + ***** + * + * NTREG - Window registry file reader / writer library + * 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 + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * See file LGPL.txt for the full license. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ntreg.h" + +/* Set to abort() and debug on more critical errors */ +#define DOCORE 1 + +#define ZEROFILL 1 /* Fill blocks with zeroes when allocating and deallocating */ +#define ZEROFILLONLOAD 0 /* Fill blocks marked as unused/deallocated with zeroes on load. FOR DEBUG */ + +const char ntreg_version[] = "ntreg lib routines, v0.95 110511, (c) Petter N Hagen"; + +const char *val_types[REG_MAX+1] = { + "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", /* 0 - 4 */ + "REG_DWORD_BIG_ENDIAN", "REG_LINK", /* 5 - 6 */ + "REG_MULTI_SZ", "REG_RESOUCE_LIST", "REG_FULL_RES_DESC", "REG_RES_REQ", /* 7 - 10 */ + "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; + + if (!str) + return 0 ; + + CREATE( str_new, char, strlen(str) + 1 ); + strcpy( str_new, str ); + 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) +{ + + printf("%s",prmpt); + + fgets(ibuf,maxlen+1,stdin); + + ibuf[strlen(ibuf)-1] = 0; + + return(strlen(ibuf)); +} + +/* Print len number of hexbytes */ + +void hexprnt(char *s, unsigned char *bytes, int len) +{ +int i; + + printf("%s",s); + for (i = 0; i < len; i++) { + printf("%02x ",bytes[i]); + } + printf("\n"); +} + +/* HexDump all or a part of some buffer */ + +void hexdump(char *hbuf, int start, int stop, int ascii) +{ + char c; + int diff,i; + + while (start < stop ) { + + diff = stop - start; + if (diff > 16) diff = 16; + + printf(":%05X ",start); + + for (i = 0; i < diff; i++) { + printf("%02X ",(unsigned char)*(hbuf+start+i)); + } + if (ascii) { + for (i = diff; i < 16; i++) printf(" "); + for (i = 0; i < diff; i++) { + c = *(hbuf+start+i); + printf("%c", isprint(c) ? c : '.'); + } + } + printf("\n"); + start += 16; + } +} + +/* General search routine, find something in something else */ +int find_in_buf(char *buf, char *what, int sz, int len, int start) +{ + int i; + + for (; start < sz; start++) { + for (i = 0; i < len; i++) { + if (*(buf+start+i) != *(what+i)) break; + } + if (i == len) return(start); + } + return(0); +} + +/* Get INTEGER from memory. This is probably low-endian specific? */ +int get_int( char *array ) +{ + return ((array[0]&0xff) + ((array[1]<<8)&0xff00) + + ((array[2]<<16)&0xff0000) + + ((array[3]<<24)&0xff000000)); +} + + +/* Quick and dirty UNICODE to std. ascii */ + +void cheap_uni2ascii(char *src, char *dest, int l) +{ + + for (; l > 0; l -=2) { + *dest = *src; + dest++; src +=2; + } + *dest = 0; +} + + +/* Quick and dirty ascii to unicode */ + +void cheap_ascii2uni(char *src, char *dest, int l) +{ + for (; l > 0; l--) { + *dest++ = *src++; + *dest++ = 0; + + } +} + +void skipspace(char **c) +{ + while( **c == ' ' ) (*c)++; +} + +int gethex(char **c) +{ + int value; + + skipspace(c); + + if (!(**c)) return(0); + + sscanf(*c,"%x",&value); + + while( **c != ' ' && (**c)) (*c)++; + + return(value); +} + +/* Get a string of HEX bytes (space separated), + * or if first char is ' get an ASCII string instead. + */ + +int gethexorstr(char **c, char *wb) +{ + int l = 0; + + skipspace(c); + + if ( **c == '\'') { + (*c)++; + while ( **c ) { + *(wb++) = *((*c)++); + l++; + } + } else { + do { + *(wb++) = gethex(c); + l++; + skipspace(c); + } while ( **c ); + } + return(l); +} + +/* Simple buffer debugger, returns 1 if buffer dirty/edited */ + +int debugit(char *buf, int sz) +{ + + + char inbuf[100],whatbuf[100],*bp; + + int dirty=0,to,from,l,i,j,wlen,cofs = 0; + + printf("Buffer debugger. '?' for help.\n"); + + while (1) { + l = fmyinput(".",inbuf,90); + bp = inbuf; + + skipspace(&bp); + + if (l > 0 && *bp) { + switch(*bp) { + case 'd' : + bp++; + if (*bp) { + from = gethex(&bp); + to = gethex(&bp); + } else { + from = cofs; to = 0; + } + if (to == 0) to = from + 0x100; + if (to > sz) to = sz; + hexdump(buf,from,to,1); + cofs = to; + break; + case 'a' : + bp++; + if (*bp) { + from = gethex(&bp); + to = gethex(&bp); + } else { + from = cofs; to = 0; + } + if (to == 0) to = from + 0x100; + if (to > sz) to = sz; + hexdump(buf,from,to,0); + cofs = to; + break; + case 'q': + return(0); + break; + case 's': + if (!dirty) printf("Buffer has not changed, no need to write..\n"); + return(dirty); + break; + case 'h': + bp++; + if (*bp == 'a') { + from = 0; + to = sz; + bp++; + } else { + from = gethex(&bp); + to = gethex(&bp); + } + wlen = gethexorstr(&bp,whatbuf); + if (to > sz) to = sz; + printf("from: %x, to: %x, wlen: %d\n",from,to,wlen); + for (i = from; i < to; i++) { + for (j = 0; j < wlen; j++) { + if ( *(buf+i+j) != *(whatbuf+j)) break; + } + if (j == wlen) printf("%06x ",i); + } + printf("\n"); + break; + case ':': + bp++; + if (!*bp) break; + from = gethex(&bp); + wlen = gethexorstr(&bp,whatbuf); + + printf("from: %x, wlen: %d\n",from,wlen); + + memcpy(buf+from,whatbuf,wlen); + dirty = 1; + break; + + case '?': + printf("d [] [] - dump buffer within range\n"); + printf("a [] [] - same as d, but without ascii-part (for cut'n'paste)\n"); + printf(": [ ...] - change bytes\n"); + printf("h [ ...] - hunt (search) for bytes\n"); + printf("ha [ etc. you may give 'string to enter/search a string\n"); + break; + default: + printf("?\n"); + break; + } + } + } +} + + +/* ========================================================================= */ + +/* The following routines are mostly for debugging, I used it + * much during discovery. the -t command line option uses it, + * also the 'st' and 's' from the editor & hexdebugger. + * All offsets shown in these are unadjusted (ie you must add + * headerpage (most often 0x1000) to get file offset) + */ + +/* Parse the nk datablock + * vofs = offset into struct (after size linkage) + */ +void parse_nk(struct hive *hdesc, int vofs, int blen) +{ + + struct nk_key *key; + int i; + + printf("== nk at offset %0x\n",vofs); + + /* #define D_OFFS2(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */ +#define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) + + key = (struct nk_key *)(hdesc->buffer + vofs); + 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 + 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 + 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 + 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); + + printf("%04x Key name: <",D_OFFS(keyname) ); + for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]); + printf(">\n== End of key info.\n"); + +} + +/* Parse the vk datablock + * vofs = offset into struct (after size linkage) + */ +void parse_vk(struct hive *hdesc, int vofs, int blen) +{ + struct vk_key *key; + int i; + + printf("== vk at offset %0x\n",vofs); + + + key = (struct vk_key *)(hdesc->buffer + vofs); + printf("%04x name length = %d (0x%0x)\n", D_OFFS(len_name), + 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 + 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)") ) ; + + printf("%04x flag = 0x%0x\n",D_OFFS(flag),key->flag); + printf("%04x *unused?* = 0x%0x\n",D_OFFS(dummy1),key->dummy1); + + printf("%04x Key name: <",D_OFFS(keyname) ); + for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]); + printf(">\n== End of key info.\n"); + +} + +/* Parse the sk datablock + * Gee, this is the security info. Who cares? *evil grin* + * vofs = offset into struct (after size linkage) + */ +void parse_sk(struct hive *hdesc, int vofs, int blen) +{ + struct sk_key *key; + /* int i; */ + + printf("== sk at offset %0x\n",vofs); + + 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 + 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), + key->len_sk,key->len_sk); + + printf("== End of key info.\n"); + +} + + +/* Parse the lf datablock (>4.0 'nk' offsets lookuptable) + * vofs = offset into struct (after size linkage) + */ +void parse_lf(struct hive *hdesc, int vofs, int blen) +{ + struct lf_key *key; + int i; + + printf("== lf at offset %0x\n",vofs); + + key = (struct lf_key *)(hdesc->buffer + vofs); + printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); + + 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 + 0x1000, + key->hash[i].name[0], + key->hash[i].name[1], + key->hash[i].name[2], + key->hash[i].name[3] ); + } + + printf("== End of key info.\n"); + +} + +/* Parse the lh datablock (WinXP offsets lookuptable) + * vofs = offset into struct (after size linkage) + * The hash is most likely a base 37 conversion of the name string + */ +void parse_lh(struct hive *hdesc, int vofs, int blen) +{ + struct lf_key *key; + int i; + + printf("== lh at offset %0x\n",vofs); + + key = (struct lf_key *)(hdesc->buffer + vofs); + printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); + + 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 + 0x1000, + key->lh_hash[i].hash ); + } + + printf("== End of key info.\n"); + +} + + +/* Parse the li datablock (3.x 'nk' offsets list) + * vofs = offset into struct (after size linkage) + */ +void parse_li(struct hive *hdesc, int vofs, int blen) +{ + struct li_key *key; + int i; + + printf("== li at offset %0x\n",vofs); + + /* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */ + + key = (struct li_key *)(hdesc->buffer + vofs); + printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); + + 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 + 0x1000); + } + printf("== End of key info.\n"); + +} + +/* Parse the ri subindex-datablock + * (Used to list li/lf/lh's when ~>500keys) + * vofs = offset into struct (after size linkage) + */ +void parse_ri(struct hive *hdesc, int vofs, int blen) +{ + struct ri_key *key; + int i; + + printf("== ri at offset %0x\n",vofs); + + /* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */ + + key = (struct ri_key *)(hdesc->buffer + vofs); + printf("%04x number of subindices = %d\n", D_OFFS(no_lis), key->no_lis ); + + 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 + 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) + */ + +int parse_block(struct hive *hdesc, int vofs,int verbose) +{ + unsigned short id; + int seglen; + + 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("parse_block: FATAL! Zero data block size! (not registry or corrupt file?)\n"); + debugit(hdesc->buffer,hdesc->size); + return(0); + } + + if (seglen < 0) { + seglen = -seglen; + hdesc->usetot += seglen; + hdesc->useblk++; + if (verbose) { + printf("USED BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen); + /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ + } + } else { + hdesc->unusetot += seglen; + hdesc->unuseblk++; + /* Useful to zero blocks we think are empty when debugging.. */ +#if ZEROFILLONLOAD + bzero(hdesc->buffer+vofs+4,seglen-4); +#endif + + if (verbose) { + printf("FREE BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen); + /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ + } + } + + + vofs += 4; + id = (*(hdesc->buffer + vofs)<<8) + *(hdesc->buffer+vofs+1); + + if (verbose > 1) { + switch (id) { + case 0x6e6b: /* nk */ + parse_nk(hdesc, vofs, seglen); + break; + case 0x766b: /* vk */ + parse_vk(hdesc, vofs, seglen); + break; + case 0x6c66: /* lf */ + parse_lf(hdesc, vofs, seglen); + break; + case 0x6c68: /* lh */ + parse_lh(hdesc, vofs, seglen); + break; + case 0x6c69: /* li */ + parse_li(hdesc, vofs, seglen); + break; + case 0x736b: /* sk */ + parse_sk(hdesc, vofs, seglen); + break; + 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; + } + } + return(seglen); +} + +/* ================================================================ */ +/* Scan and allocation routines */ + +/* Find start of page given a current pointer into the buffer + * hdesc = hive + * vofs = offset pointer into buffer + * returns: offset to start of page (and page header) + */ + +int find_page_start(struct hive *hdesc, int vofs) +{ + int r,prev; + struct hbin_page *h; + + /* Again, assume start at 0x1000 */ + + r = 0x1000; + while (r < hdesc->size) { + prev = r; + h = (struct hbin_page *)(hdesc->buffer + r); + if (h->id != 0x6E696268) return(0); + if (h->ofs_next == 0) { + printf("find_page_start: zero len or ofs_next found in page at 0x%x\n",r); + return(0); + } + r += h->ofs_next; + if (r > vofs) return (prev); + } + return(0); +} + +/* Find free space in page + * size = requested size in bytes + * pofs = offset to start of actual page header + * returns: offset to free block, or 0 for error + */ + +#define FB_DEBUG 0 + +int find_free_blk(struct hive *hdesc, int pofs, int size) +{ + int vofs = pofs + 0x20; + int seglen; + struct hbin_page *p; + + p = (struct hbin_page *)(hdesc->buffer + pofs); + + while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL)) { + + 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); + } + + if (seglen < 0) { + seglen = -seglen; +#if FB_DEBUG + 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 + if (vofs >0x400000) printf("FREE BLOCK!\n"); +#endif + /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ + if (seglen >= size) { +#if FB_DEBUG + if (vofs >0x400000) printf("find_free_blk: found size %d block at 0x%x\n",seglen,vofs); +#endif +#if 0 + if (vofs == 0x19fb8) { + printf("find_free_blk: vofs = %x, seglen = %x\n",vofs,seglen); + debugit(hdesc->buffer,hdesc->size); + abort(); + } +#endif + return(vofs); + } + } + vofs += seglen; + } + return(0); + +} + +#undef FB_DEBUG + +/* Search pages from start to find free block + * hdesc - hive + * size - space requested, in bytes + * returns: offset to free block, 0 if not found or error + */ + +int find_free(struct hive *hdesc, int size) +{ + int r,blk; + struct hbin_page *h; + + /* Align to 8 byte boundary */ + if (size & 7) size += (8 - (size & 7)); + + /* Again, assume start at 0x1000 */ + + r = 0x1000; + while (r < hdesc->endofs) { + h = (struct hbin_page *)(hdesc->buffer + r); + if (h->id != 0x6E696268) return(0); + if (h->ofs_next == 0) { + printf("find_free: zero len or ofs_next found in page at 0x%x\n",r); + return(0); + } + blk = find_free_blk(hdesc,r,size); + if (blk) return (blk); + r += h->ofs_next; + } + 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. + */ + +int alloc_block(struct hive *hdesc, int ofs, int size) +{ + int pofs = 0; + int blk = 0; + int newbin; + int trail, trailsize, oldsz; + + if (hdesc->state & HMODE_NOALLOC) { + printf("\nERROR: alloc_block: Hive <%s> is in no allocation safe mode," + "new space not allocated. Operation will fail!\n", hdesc->filename); + return(0); + } + + size += 4; /* Add linkage */ + if (size & 7) size += (8 - (size & 7)); + + /* Check current page first */ + if (ofs) { + pofs = find_page_start(hdesc,ofs); + blk = find_free_blk(hdesc,pofs,size); + } + + /* Then check whole hive */ + if (!blk) { + blk = find_free(hdesc,size); + } + + if (blk) { /* Got the space */ + oldsz = get_int(hdesc->buffer+blk); +#if 0 + printf("Block at : %x\n",blk); + printf("Old block size is: %x\n",oldsz); + printf("New block size is: %x\n",size); +#endif + trailsize = oldsz - size; + + if (trailsize == 4) { + trailsize = 0; + size += 4; + } + + #if 1 + if (trailsize & 7) { /* Trail must be 8 aligned */ + trailsize -= (8 - (trailsize & 7)); + size += (8 - (trailsize & 7)); + } + if (trailsize == 4) { + trailsize = 0; + size += 4; + } +#endif + +#if 0 + printf("trail after comp: %x\n",trailsize); + printf("size after comp: %x\n",size); +#endif + + /* Now change pointers on this to reflect new size */ + *(int *)((hdesc->buffer)+blk) = -(size); + /* If the fit was exact (unused block was same size as wee need) + * there is no need for more, else make free block after end + * of newly allocated one */ + + hdesc->useblk++; + hdesc->unuseblk--; + hdesc->usetot += size; + hdesc->unusetot -= size; + + if (trailsize) { + trail = blk + size; + + *(int *)((hdesc->buffer)+trail) = (int)trailsize; + + hdesc->useblk++; /* This will keep blockcount */ + hdesc->unuseblk--; + hdesc->usetot += 4; /* But account for more linkage bytes */ + hdesc->unusetot -= 4; + + } + /* Clear the block data, makes it easier to debug */ +#if ZEROFILL + bzero( (void *)(hdesc->buffer+blk+4), size-4); +#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, 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); +} + +/* Free a block in registry + * hdesc - hive + * blk - offset of block, MUST POINT TO THE LINKAGE! + * returns bytes freed (incl linkage bytes) or 0 if fail + * Will CHANGE HIVE IF SUCCESSFUL (changes linkage) + */ + +#define FB_DEBUG 0 + +int free_block(struct hive *hdesc, int blk) +{ + int pofs,vofs,seglen,prev,next,nextsz,prevsz,size; + struct hbin_page *p; + + if (hdesc->state & HMODE_NOALLOC) { + printf("free_block: ERROR: Hive %s is in no allocation safe mode," + "space not freed. Operation will fail!\n", hdesc->filename); + return(0); + } + + size = get_int(hdesc->buffer+blk); + if (size >= 0) { + printf("free_block: trying to free already free block!\n"); +#ifdef DOCORE + printf("blk = %x\n",blk); + debugit(hdesc->buffer,hdesc->size); + abort(); +#endif + return(0); + } + size = -size; + + /* So, we must find start of the block BEFORE us */ + pofs = find_page_start(hdesc,blk); + if (!pofs) return(0); + + p = (struct hbin_page *)(hdesc->buffer + pofs); + vofs = pofs + 0x20; + + prevsz = -32; + + if (vofs != blk) { /* Block is not at start of page? */ + while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL) ) { + + seglen = get_int(hdesc->buffer+vofs); + + if (seglen == 0) { + printf("free_block: EEEK! Zero data block size! (not registry or corrupt file?)\n"); + debugit(hdesc->buffer,hdesc->size); + return(0); + } + + if (seglen < 0) { + seglen = -seglen; + /* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */ + } + prev = vofs; + vofs += seglen; + if (vofs == blk) break; + } + + if (vofs != blk) { + printf("free_block: ran off end of page!?!? Error in chains?\n"); +#ifdef DOCORE + printf("vofs = %x, pofs = %x, blk = %x\n",vofs,pofs,blk); + debugit(hdesc->buffer,hdesc->size); + abort(); +#endif + return(0); + } + + prevsz = get_int(hdesc->buffer+prev); + + } + + /* We also need details on next block (unless at end of page) */ + next = blk + size; + + nextsz = 0; + if (next-pofs < (p->ofs_next - HBIN_ENDFILL) ) nextsz = get_int(hdesc->buffer+next); + +#if 0 + printf("offset prev : %x , blk: %x , next: %x\n",prev,blk,next); + printf("size prev : %x , blk: %x , next: %x\n",prevsz,size,nextsz); +#endif + + /* Now check if next block is free, if so merge it with the one to be freed */ + if ( nextsz > 0) { +#if 0 + printf("Swallow next\n"); +#endif + size += nextsz; /* Swallow it in current block */ + hdesc->useblk--; + hdesc->usetot -= 4; + hdesc->unusetot -= 4; /* FIXME !??!?? */ + } + + /* Now free the block (possibly with ajusted size as above) */ +#if ZEROFILL + bzero( (void *)(hdesc->buffer+blk), size); +#endif + + *(int *)((hdesc->buffer)+blk) = (int)size; + hdesc->usetot -= size; + hdesc->unusetot -= size; /* FIXME !?!? */ + hdesc->unuseblk--; + + hdesc->state |= HMODE_DIRTY; + + /* Check if previous block is also free, if so, merge.. */ + if (prevsz > 0) { +#if 0 + printf("Swallow prev\n"); +#endif + hdesc->usetot -= prevsz; + hdesc->unusetot += prevsz; + prevsz += size; + /* And swallow current.. */ +#if ZEROFILL + bzero( (void *)(hdesc->buffer+prev), prevsz); +#endif + *(int *)((hdesc->buffer)+prev) = (int)prevsz; + hdesc->useblk--; + return(prevsz); + } + return(size); +} + + + + + +/* ================================================================ */ + +/* ** Registry manipulation routines ** */ + + + +/* "directory scan", return next name/pointer of a subkey on each call + * nkofs = offset to directory to scan + * lfofs = pointer to int to hold the current scan position, + * set position to 0 to start. + * sptr = pointer to struct to hold a single result + * returns: -1 = error. 0 = end of key. 1 = more subkeys to scan + * NOTE: caller must free the name-buffer (struct ex_data *name) + */ +int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr) +{ + struct nk_key *key, *newnkkey; + int newnkofs; + struct lf_key *lfkey; + struct li_key *likey; + struct ri_key *rikey; + + + if (!nkofs) return(-1); + key = (struct nk_key *)(hdesc->buffer + nkofs); + if (key->id != 0x6b6e) { + printf("ex_next error: Not a 'nk' node at 0x%0x\n",nkofs); + return(-1); + } + +#define EXNDEBUG 0 + + lfkey = (struct lf_key *)(hdesc->buffer + key->ofs_lf + 0x1004); + rikey = (struct ri_key *)(hdesc->buffer + key->ofs_lf + 0x1004); + + if (rikey->id == 0x6972) { /* Is it extended 'ri'-block? */ +#if EXNDEBUG + printf("%d , %d\n",*countri,*count); +#endif + if (*countri < 0 || *countri >= rikey->no_lis) { /* End of ri's? */ + return(0); + } + /* Get the li of lf-struct that's current based on countri */ + likey = (struct li_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ; + if (likey->id == 0x696c) { + newnkofs = likey->hash[*count].ofs_nk + 0x1000; + } else { + lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ; + newnkofs = lfkey->hash[*count].ofs_nk + 0x1000; + } + + /* Check if current li/lf is exhausted */ +#if EXNDEBUG + printf("likey->no_keys = %d\n",likey->no_keys); +#endif + if (*count >= likey->no_keys-1) { /* Last legal entry in li list? */ + (*countri)++; /* Bump up ri count so we take next ri entry next time */ + (*count) = -1; /* Reset li traverse counter for next round, not used later here */ + } + } else { /* Plain handler */ + if (key->no_subkeys <= 0 || *count >= key->no_subkeys) { + return(0); + } + if (lfkey->id == 0x696c) { /* Is it 3.x 'li' instead? */ + likey = (struct li_key *)(hdesc->buffer + key->ofs_lf + 0x1004); + newnkofs = likey->hash[*count].ofs_nk + 0x1000; + } else { + newnkofs = lfkey->hash[*count].ofs_nk + 0x1000; + } + } + + sptr->nkoffs = newnkofs; + newnkkey = (struct nk_key *)(hdesc->buffer + newnkofs + 4); + sptr->nk = newnkkey; + + if (newnkkey->id != 0x6b6e) { + printf("ex_next: ERROR: not 'nk' node at 0x%0x\n",newnkofs); + + return(-1); + } 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 = string_regw2prog(newnkkey->keyname, newnkkey->len_name); + } + } /* if */ + (*count)++; + return(1); + /* return( *count <= key->no_subkeys); */ +} + +/* "directory scan" for VALUES, return next name/pointer of a value on each call + * nkofs = offset to directory to scan + * lfofs = pointer to int to hold the current scan position, + * set position to 0 to start. + * sptr = pointer to struct to hold a single result + * returns: -1 = error. 0 = end of key. 1 = more values to scan + * NOTE: caller must free the name-buffer (struct vex_data *name) + */ +int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr) +{ + struct nk_key *key /* , *newnkkey */ ; + int vkofs,vlistofs; + int *vlistkey; + struct vk_key *vkkey; + + + if (!nkofs) return(-1); + key = (struct nk_key *)(hdesc->buffer + nkofs); + if (key->id != 0x6b6e) { + printf("ex_next_v error: Not a 'nk' node at 0x%0x\n",nkofs); + return(-1); + } + + if (key->no_values <= 0 || *count >= key->no_values) { + return(0); + } + + vlistofs = key->ofs_vallist + 0x1004; + vlistkey = (int *)(hdesc->buffer + vlistofs); + + vkofs = vlistkey[*count] + 0x1004; + vkkey = (struct vk_key *)(hdesc->buffer + vkofs); + if (vkkey->id != 0x6b76) { + printf("ex_next_v: hit non valuekey (vk) node during scan at offs 0x%0x\n",vkofs); + return(-1); + } + + /* parse_vk(hdesc, vkofs, 4); */ + + sptr->vk = vkkey; + sptr->vkoffs = vkofs; + sptr->name = 0; + sptr->size = (vkkey->len_data & 0x7fffffff); + + if (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->type = vkkey->val_type; + + if (sptr->size) { + if (vkkey->val_type == REG_DWORD) { + if (vkkey->len_data & 0x80000000) { + sptr->val = (int)(vkkey->ofs_data); + } + } + } +#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); + sptr->size = 4; + sptr->type = REG_DWORD; + } else { + sptr->val = 0; + sptr->size = 0; + } +#endif + + (*count)++; + return( *count <= key->no_values ); +} + +/* traceback - trace nk's back to root, + * building path string as we go. + * nkofs = offset to nk-node + * path = pointer to pathstring-buffer + * maxlen = max length of path-buffer + * return: length of path string + */ + +int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen) +{ + /* int newnkofs; */ + struct nk_key *key; + char tmp[ABSPATHLEN+1]; + char *keyname; + int len_name; + + maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN); + + key = (struct nk_key *)(hdesc->buffer + nkofs); + + if (key->id != 0x6b6e) { + printf("get_abs_path: Not a 'nk' node!\n"); + return(0); + } + + if (key->type == KEY_ROOT) { /* We're at the root */ + return(strlen(path)); + } + + strncpy(tmp,path,ABSPATHLEN-1); + + 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,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 */ +} + + +/* Value index table lookup + * hdesc - hive as usual + * vlistofs - offset of table + * name - value name to look for + * returns index into table or -1 if err + */ + +int vlist_find(struct hive *hdesc, int vlistofs, int numval, char *name, int type) +{ + 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 == '@' && len == 1) { /* @ is alias for nameless value */ + 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(approx); + +} + +/* Recursevely follow 'nk'-nodes based on a path-string, + * returning offset of last 'nk' or 'vk' + * vofs - offset to start node + * path - null-terminated pathname (relative to vofs, \ is separator) + * type - type to return TPF_??, see ntreg.h + * return: offset to nk or vk (or NULL if not found) + */ + +int trav_path(struct hive *hdesc, int vofs, char *path, int type) +{ + struct nk_key *key, *newnkkey; + struct lf_key *lfkey; + struct li_key *likey; + struct ri_key *rikey; + + int32_t *vlistkey; + int newnkofs, plen, i, lfofs, vlistofs, adjust, r, ricnt, subs; + char *buf; + char part[ABSPATHLEN+1]; + char *partptr; + + 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 ( !(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); + + if (key->id != 0x6b6e) { + printf("trav_path: Error: Not a 'nk' node!\n"); + return(0); + } + + 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 ((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); + if (i != -1) { + return(vlistkey[i] + 0x1000); + } + } + + 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); + + if (lfkey->id == 0x6972) { /* ri struct need special parsing */ + /* Prime loop state */ + + rikey = (struct ri_key *)lfkey; + ricnt = rikey->no_lis; + r = 0; + likey = (struct li_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; + subs = likey->no_keys; + if (likey->id != 0x696c) { /* Bwah, not li anyway, XP uses lh usually which is actually smarter */ + lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; + likey = NULL; + } + } else { + if (lfkey->id == 0x696c) { /* li? */ + likey = (struct li_key *)(buf + lfofs); + } 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; + else newnkofs = lfkey->hash[i].ofs_nk + 0x1004; + newnkkey = (struct nk_key *)(buf + newnkofs); + if (newnkkey->id != 0x6b6e) { + printf("ERROR: not 'nk' node! (strange?)\n"); + } else { + if (newnkkey->len_name <= 0) { + printf("[No name]\n"); + } 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)); + } + } + } /* if id OK */ + } /* hash loop */ + r++; + if (ricnt && r < ricnt) { + newnkofs = rikey->hash[r].ofs_li; + likey = (struct li_key *)( hdesc->buffer + newnkofs + 0x1004 ) ; + subs = likey->no_keys; + if (likey->id != 0x696c) { /* Bwah, not li anyway, XP uses lh usually which is actually smarter */ + lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; + likey = NULL; + } + } + } while (r < ricnt && ricnt); + free(partw); + + } /* if subkeys */ + + /* Not found */ + return(0); +} + + +/* ls - list a 'nk' nodes subkeys and values + * vofs - offset to start of data (skipping block linkage) + * type - 0 = full, 1 = keys only. 2 = values only + */ +void nk_ls(struct hive *hdesc, char *path, int vofs, int type) +{ + struct nk_key *key; + int nkofs; + struct ex_data ex; + struct vex_data vex; + int count = 0, countri = 0; + + + nkofs = trav_path(hdesc, vofs, path, 0); + + if(!nkofs) { + printf("nk_ls: Key <%s> not found\n",path); + return; + } + nkofs += 4; + + key = (struct nk_key *)(hdesc->buffer + nkofs); + VERBF(hdesc,"ls of node at offset 0x%0x\n",nkofs); + + if (key->id != 0x6b6e) { + printf("Error: Not a 'nk' node!\n"); + + debugit(hdesc->buffer,hdesc->size); + + } + + printf("Node has %d subkeys and %d values",key->no_subkeys,key->no_values); + if (key->len_classnam) printf(", and class-data of %d bytes",key->len_classnam); + printf("\n"); + + if (key->no_subkeys) { + printf(" key name\n"); + while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) { + if (!(hdesc->state & HMODE_VERBOSE)) printf("%c <%s>\n", (ex.nk->len_classnam)?'*':' ',ex.name); + else printf("[%6x] %c <%s>\n", ex.nkoffs, (ex.nk->len_classnam)?'*':' ',ex.name); + FREE(ex.name); + } + } + count = 0; + 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 - 4, vex.size, + (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name); + else + printf("%6d %-16s <%s>", vex.size, + (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name); + + if (vex.type == REG_DWORD) printf(" %*d [0x%x]",25-(int)strlen(vex.name),vex.val , vex.val); + printf("\n"); + FREE(vex.name); + } + } +} + +/* Get the type of a value */ +int get_val_type(struct hive *hdesc, int vofs, char *path, int exact) +{ + struct vk_key *vkkey; + int vkofs; + + vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK); + if (!vkofs) { + return -1; + } + vkofs +=4; + vkkey = (struct vk_key *)(hdesc->buffer + vkofs); + + return(vkkey->val_type); +} + + +/* Get len of a value, given current key + path */ +int get_val_len(struct hive *hdesc, int vofs, char *path, int exact) +{ + struct vk_key *vkkey; + int vkofs; + int len; + + vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK); + if (!vkofs) { + return -1; + } + vkofs +=4; + vkkey = (struct vk_key *)(hdesc->buffer + vkofs); + + len = vkkey->len_data & 0x7fffffff; + + if ( vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case, return size of 4 (dword) */ + len = 4; + } + + return(len); +} + +/* Get void-pointer to value-data, also if inline. + * If val_type != 0 a check for correct value type is done + * Caller must keep track of value's length (call function above to get it) + */ +void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact) +{ + 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 && (exact & TPF_VK_SHORT)) { /* Special inline case (len = 0x80000000) */ + return(&vkkey->val_type); /* Data (4 bytes?) in type field */ + } + + if (val_type && vkkey->val_type && (vkkey->val_type) != val_type) { + printf("Value <%s> is not of correct type!\n",path); +#if DOCORE + abort(); +#endif + return NULL; + } + + /* Negative len is inline, return ptr to offset-field which in + * this case contains the data itself + */ + if (vkkey->len_data & 0x80000000) return(&vkkey->ofs_data); + /* Normal return, return data pointer */ + return(hdesc->buffer + vkkey->ofs_data + 0x1004); +} + + +/* Get and copy key data (if any) to buffer + * if kv==NULL will allocate needed return struct & buffer + * else will use buffer allocated for it (if it fits) + * return len+data or NULL if not found (or other error) + * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice. + */ +struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv, + int vofs, char *path, int type, int exact ) +{ + 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); + + /* Allocate space for data + header, or use supplied buffer */ + if (kv) { + kr = kv; + } else { + ALLOC(kr,1,l*sizeof(int)+4); + } + + kr->len = 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); +} + +/* DWORDs are so common that I make a small function to get it easily */ + +int get_dword(struct hive *hdesc, int vofs, char *path, int exact) +{ + struct keyval *v; + int dword; + + v = get_val2buf(hdesc, NULL, vofs, path, REG_DWORD, exact | TPF_VK); + if (!v) return(-1); /* well... -1 COULD BE THE STORED VALUE TOO */ + + dword = (int)v->data; + + FREE(v); + + return(dword); + +} + +/* Sanity checker when transferring data into a block + * ofs = offset to data block, point to start of actual datablock linkage + * data = data to copy + * size = size of data to copy + */ + +int fill_block(struct hive *hdesc, int ofs, void *data, int size) +{ + int blksize; + + blksize = get_int(hdesc->buffer + ofs); + blksize = -blksize; + +#if 0 + printf("fill_block: ofs = %x - %x, size = %x, blksize = %x\n",ofs,ofs+size,size,blksize); +#endif + /* if (blksize < size || ( (ofs & 0xfffff000) != ((ofs+size) & 0xfffff000) )) { */ + if (blksize < size) { + printf("fill_block: ERROR: block to small for data: ofs = %x, size = %x, blksize = %x\n",ofs,size,blksize); + debugit(hdesc->buffer,hdesc->size); + abort(); + } + + memcpy(hdesc->buffer + ofs + 4, data, size); + return(0); +} + + +/* Free actual data of a value, and update value descriptor + * hdesc - hive + * vofs - current value offset + */ + +int free_val_data(struct hive *hdesc, int vkofs) +{ + struct vk_key *vkkey; + struct db_key *db; + int len,i,blockofs,blocksize,parts,list; + + + vkkey = (struct vk_key *)(hdesc->buffer + vkofs); + + 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 */ + + + vkkey->len_data = 0; + vkkey->ofs_data = 0; + + return(vkofs); + +} + + +/* Allocate data for value. Frees old data (if any) which will be destroyed + * hdesc - hive + * vofs - current key + * path - path to value + * size - size of data + * Returns: 0 - error, >0 pointer to actual dataspace + */ + +int alloc_val_data(struct hive *hdesc, int vofs, char *path, int size,int exact) +{ + struct vk_key *vkkey; + struct db_key *db; + int vkofs,dbofs,listofs,blockofs,blocksize,parts; + int datablk,i; + int *ptr; + + vkofs = trav_path(hdesc,vofs,path,exact); + if (!vkofs) { + return (0); + } + + 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 */ + 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); + + 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); + + + + /* Link in new datablock */ + if ( !(size & 0x80000000)) vkkey->ofs_data = datablk - 0x1000; + vkkey->len_data = size; + + return(datablk + 4); +} + + +/* Add a value to a key. + * Just add the metadata (empty value), to put data into it, use + * put_buf2val afterwards + * hdesc - hive + * nkofs - current key + * name - name of value + * type - type of value + * returns: 0 err, >0 offset to value metadata + */ + +struct vk_key *add_value(struct hive *hdesc, int nkofs, char *name, int type) +{ + struct nk_key *nk; + int oldvlist = 0, newvlist, newvkofs; + struct vk_key *newvkkey; + char *blank=""; + + if (!name || !*name) return(NULL); + + + nk = (struct nk_key *)(hdesc->buffer + nkofs); + if (nk->id != 0x6b6e) { + printf("add_value: Key pointer not to 'nk' node!\n"); + return(NULL); + } + + 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); + } + + if (!strcmp(name,"@")) name = blank; + + if (nk->no_values) oldvlist = nk->ofs_vallist; + + newvlist = alloc_block(hdesc, nkofs, nk->no_values * 4 + 4); + if (!newvlist) { + 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); + } + + /* Allocate value descriptor including its name */ + newvkofs = alloc_block(hdesc, newvlist, sizeof(struct vk_key) + strlen(name)); + if (!newvkofs) { + printf("add_value: failed to allocate value descriptor\n"); + free_block(hdesc, newvlist); + 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); + + /* Add pointer in value list */ + *(int *)(hdesc->buffer + newvlist + 4 + (nk->no_values * 4)) = newvkofs - 0x1000; + + /* Fill in vk struct */ + newvkkey->id = 0x6b76; + newvkkey->len_name = strlen(name); + if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) { + newvkkey->len_data = 0x80000004; /* Prime the DWORD inline stuff */ + } else { + newvkkey->len_data = 0x80000000; /* Default inline zero size */ + } + newvkkey->ofs_data = 0; + newvkkey->val_type = type; + newvkkey->flag = newvkkey->len_name ? 1 : 0; /* Seems to be 1, but 0 for no name default value */ + newvkkey->dummy1 = 0; + memcpy((char *)&newvkkey->keyname, name, newvkkey->len_name); /* And copy name */ + + /* Finally update the key and free the old valuelist */ + nk->no_values++; + nk->ofs_vallist = newvlist - 0x1000; + if (oldvlist) free_block(hdesc,oldvlist + 0x1000); + + return(newvkkey); + +} + +/* Remove a vk-struct incl dataspace if any + * Mostly for use by higher level stuff + * hdesc - hive + * vkofs - offset to vk + */ + +void del_vk(struct hive *hdesc, int vkofs) +{ + struct vk_key *vk; + + vk = (struct vk_key *)(hdesc->buffer + vkofs); + if (vk->id != 0x6b76) { + printf("del_vk: Key pointer not to 'vk' node!\n"); + return; + } + + if ( !(vk->len_data & 0x80000000) && vk->ofs_data) { + free_val_data(hdesc, vkofs); + } + + free_block(hdesc, vkofs - 4); +} + +/* Delete all values from key (used in recursive delete) + * hdesc - yer usual hive + * nkofs - current keyoffset + */ + +void del_allvalues(struct hive *hdesc, int nkofs) +{ + int vlistofs, o, vkofs; + int32_t *vlistkey; + struct nk_key *nk; + + nk = (struct nk_key *)(hdesc->buffer + nkofs); + if (nk->id != 0x6b6e) { + printf("del_allvalues: Key pointer not to 'nk' node!\n"); + return; + } + + if (!nk->no_values) { + /* printf("del_avalues: Key has no values!\n"); */ + return; + } + + vlistofs = nk->ofs_vallist + 0x1004; + vlistkey = (int32_t *)(hdesc->buffer + vlistofs); + + /* Loop through index and delete all vk's */ + for (o = 0; o < nk->no_values; o++) { + vkofs = vlistkey[o] + 0x1004; + del_vk(hdesc, vkofs); + } + + /* Then zap the index, and update nk */ + free_block(hdesc, vlistofs-4); + nk->ofs_vallist = -1; + nk->no_values = 0; +} + + +/* Delete single value from key + * hdesc - yer usual hive + * nkofs - current keyoffset + * name - name of value to delete + * exact - NKF_EXACT to do exact match, else first match + * returns: 0 - ok, 1 - failed + */ + +int del_value(struct hive *hdesc, int nkofs, char *name, int exact) +{ + int vlistofs, slot, o, n, vkofs, newlistofs; + int32_t *vlistkey, *tmplist, *newlistkey; + struct nk_key *nk; + char *blank=""; + + if (!name || !*name) return(1); + + if (!strcmp(name,"@")) name = blank; + + nk = (struct nk_key *)(hdesc->buffer + nkofs); + if (nk->id != 0x6b6e) { + printf("del_value: Key pointer not to 'nk' node!\n"); + return(1); + } + + if (!nk->no_values) { + printf("del_value: Key has no values!\n"); + return(1); + } + + vlistofs = nk->ofs_vallist + 0x1004; + vlistkey = (int32_t *)(hdesc->buffer + vlistofs); + + slot = vlist_find(hdesc, vlistofs, nk->no_values, name, TPF_VK); + + if (slot == -1) { + printf("del_value: value %s not found!\n",name); + return(1); + } + + /* Delete vk and data */ + vkofs = vlistkey[slot] + 0x1004; + del_vk(hdesc, vkofs); + + /* Copy out old index list */ + CREATE(tmplist,int32_t,nk->no_values); + memcpy(tmplist, vlistkey, nk->no_values * sizeof(int32_t)); + + free_block(hdesc,vlistofs-4); /* Get rid of old list */ + + nk->no_values--; + + if (nk->no_values) { + newlistofs = alloc_block(hdesc, vlistofs, nk->no_values * sizeof(int32_t)); + if (!newlistofs) { + 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++) { + if (o == slot) o++; + newlistkey[n] = tmplist[o]; + } + nk->ofs_vallist = newlistofs - 0x1000; + } else { + nk->ofs_vallist = -1; + } + return(0); +} + + + + +/* Add a subkey to a key + * hdesc - usual.. + * nkofs - offset of current nk + * name - name of key to add + * return: ptr to new keystruct, or NULL + */ + +#undef AKDEBUG + +struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name) +{ + + int slot, newlfofs = 0, oldlfofs = 0, newliofs = 0; + int oldliofs = 0; + int o, n, i, onkofs, newnkofs, cmp; + int rimax, rislot, riofs, namlen; + struct ri_key *ri = NULL; + struct lf_key *newlf = NULL, *oldlf; + struct li_key *newli = NULL, *oldli; + struct nk_key *key, *newnk, *onk; + int32_t hash; + + key = (struct nk_key *)(hdesc->buffer + nkofs); + + if (key->id != 0x6b6e) { + printf("add_key: current ptr not 'nk'\n"); + return(NULL); + } + + namlen = strlen(name); + + slot = -1; + if (key->no_subkeys) { /* It already has subkeys */ + + oldlfofs = key->ofs_lf; + oldliofs = key->ofs_lf; + + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972) { + printf("add_key: index type not supported: 0x%04x\n",oldlf->id); + return(NULL); + } + + rimax = 0; ri = NULL; riofs = 0; rislot = -1; + if (oldlf->id == 0x6972) { /* Indirect index 'ri', init loop */ + riofs = key->ofs_lf; + ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); + rimax = ri->no_lis-1; + +#ifdef AKDEBUG + printf("add_key: entering 'ri' traverse, rimax = %d\n",rimax); +#endif + + oldliofs = ri->hash[rislot+1].ofs_li; + oldlfofs = ri->hash[rislot+1].ofs_li; + + } + + do { /* 'ri' loop, at least run once if no 'ri' deep index */ + + if (ri) { /* Do next 'ri' slot */ + rislot++; + oldliofs = ri->hash[rislot].ofs_li; + oldlfofs = ri->hash[rislot].ofs_li; + oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + } + + oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + +#ifdef AKDEBUG + printf("add_key: top of ri-loop: rislot = %d, rimax = %d\n",rislot,rimax); +#endif + slot = -1; + + if (oldli->id == 0x696c) { /* li */ + +#ifdef AKDEBUG + printf("add_key: li slot allocate\n"); +#endif + + FREE(newli); + ALLOC(newli, 8 + 4*oldli->no_keys + 4, 1); + newli->no_keys = oldli->no_keys; + newli->id = oldli->id; + + /* Now copy old, checking where to insert (alphabetically) */ + for (o = 0, n = 0; o < oldli->no_keys; o++,n++) { + onkofs = oldli->hash[o].ofs_nk; + onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004); + if (slot == -1) { +#if 1 + printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname); +#endif + + 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); + return(NULL); + } + if ( cmp < 0) { + slot = o; + rimax = rislot; /* Cause end of 'ri' search, too */ + n++; +#ifdef AKDEBUG + printf("add_key: li-match: slot = %d\n",o); +#endif + } + } + newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk; + } + if (slot == -1) slot = oldli->no_keys; + + } else { /* lf or lh */ + + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + + FREE(newlf); + ALLOC(newlf, 8 + 8*oldlf->no_keys + 8, 1); + newlf->no_keys = oldlf->no_keys; + newlf->id = oldlf->id; +#ifdef AKDEBUG + printf("add_key: new lf/lh no_keys: %d\n",newlf->no_keys); +#endif + + /* Now copy old, checking where to insert (alphabetically) */ + 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) { + +#if 0 + printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname); +#endif + 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); + return(NULL); + } + if ( cmp < 0 ) { + slot = o; + rimax = rislot; /* Cause end of 'ri' search, too */ + n++; +#ifdef AKDEBUG + printf("add_key: lf-match: slot = %d\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]; + } + if (slot == -1) slot = oldlf->no_keys; + } /* li else check */ + + + } while ( (rislot < rimax) && (rimax > 0)); /* 'ri' wrapper loop */ + + } else { /* Parent was empty, make new index block */ +#ifdef AKDEBUG + printf("add_key: new index!\n"); +#endif + ALLOC(newlf, 8 + 8, 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; + } /* if has keys before */ + + + /* Make and fill in new nk */ + newnkofs = alloc_block(hdesc, nkofs, sizeof(struct nk_key) + strlen(name)); + if (!newnkofs) { + printf("add_key: unable to allocate space for new key descriptor for %s!\n",name); + FREE(newlf); + 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; /* Some versions use 0x1020 a lot.. */ + newnk->ofs_parent = nkofs - 0x1004; + newnk->no_subkeys = 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; + memcpy(newnk->keyname, name, newnk->len_name); + + if (newli) { /* Handle li */ + +#if AKDEBUG + printf("add_key: li fill at slot: %d\n",slot); +#endif + + /* And put its offset into parents index list */ + newli->hash[slot].ofs_nk = newnkofs - 0x1000; + newli->no_keys++; + + /* Allocate space for our new li list and copy it into reg */ + newliofs = alloc_block(hdesc, nkofs, 8 + 4*newli->no_keys); + if (!newliofs) { + printf("add_key: unable to allocate space for new index table for %s!\n",name); + FREE(newli); + 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); + + + } else { /* lh or lf */ + +#ifdef AKDEBUG + printf("add_key: lf/lh fill at slot: %d, rislot: %d\n",slot,rislot); +#endif + /* And put its offset into parents index list */ + newlf->hash[slot].ofs_nk = newnkofs - 0x1000; + newlf->no_keys++; + if (newlf->id == 0x666c) { /* lf hash */ + newlf->hash[slot].name[0] = 0; + newlf->hash[slot].name[1] = 0; + newlf->hash[slot].name[2] = 0; + newlf->hash[slot].name[3] = 0; + strncpy(newlf->hash[slot].name, name, 4); + } else if (newlf->id == 0x686c) { /* lh. XP uses this. hashes whole name */ + for (i = 0,hash = 0; i < strlen(name); i++) { + hash *= 37; + hash += reg_touppertable[(unsigned char)name[i]]; + } + newlf->lh_hash[slot].hash = hash; + } + + /* Allocate space for our new lf list and copy it into reg */ + newlfofs = alloc_block(hdesc, nkofs, 8 + 8*newlf->no_keys); + if (!newlfofs) { + printf("add_key: unable to allocate space for new index table for %s!\n",name); + FREE(newlf); + 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); + + } /* li else */ + + + /* Update parent, and free old lf list */ + key->no_subkeys++; + if (ri) { /* ri index */ + ri->hash[rislot].ofs_li = (newlf ? newlfofs : newliofs) - 0x1000; + } else { /* Parent key */ + key->ofs_lf = (newlf ? newlfofs : newliofs) - 0x1000; + } + + if (newlf && oldlfofs) free_block(hdesc,oldlfofs + 0x1000); + if (newli && oldliofs) free_block(hdesc,oldliofs + 0x1000); + + FREE(newlf); + FREE(newli); + return(newnk); + +} + +/* Delete a subkey from a key + * hdesc - usual.. + * nkofs - offset of current nk + * name - name of key to delete (must match exactly, also case) + * return: 1 - err, 0 - ok + */ + +#undef DKDEBUG + +int del_key(struct hive *hdesc, int nkofs, char *name) +{ + + int slot = 0, newlfofs = 0, oldlfofs = 0, o, n, onkofs, delnkofs; + int oldliofs = 0, no_keys = 0, newriofs = 0; + int namlen; + int rimax, riofs, rislot; + struct ri_key *ri, *newri = NULL; + struct lf_key *newlf = NULL, *oldlf = NULL; + struct li_key *newli = NULL, *oldli = NULL; + struct nk_key *key, *onk, *delnk; + char fullpath[501]; + + key = (struct nk_key *)(hdesc->buffer + nkofs); + + 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); + } + + slot = -1; + if (!key->no_subkeys) { + printf("del_key: key has no subkeys!\n"); + return(1); + } + + oldlfofs = key->ofs_lf; + oldliofs = key->ofs_lf; + + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972) { + printf("del_key: index other than 'lf', 'li' or 'lh' not supported yet. 0x%04x\n",oldlf->id); + return(1); + } + + rimax = 0; ri = NULL; riofs = 0; + rislot = 0; + + if (oldlf->id == 0x6972) { /* Indirect index 'ri', init loop */ + riofs = key->ofs_lf; + ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); + rimax = ri->no_lis-1; + +#ifdef DKDEBUG + printf("del_key: entering 'ri' traverse, rimax = %d\n",rimax); +#endif + + rislot = -1; /* Starts at slot 0 below */ + + } + + do { /* 'ri' loop, at least run once if no 'ri' deep index */ + + if (ri) { /* Do next 'ri' slot */ + rislot++; + oldliofs = ri->hash[rislot].ofs_li; + oldlfofs = ri->hash[rislot].ofs_li; + } + + oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004); + oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004); + +#ifdef DKDEBUG + printf("del_key: top of ri-loop: rislot = %d\n",rislot); +#endif + slot = -1; + + if (oldlf->id == 0x696c) { /* 'li' handler */ +#ifdef DKDEBUG + printf("del_key: li handler\n"); +#endif + + FREE(newli); + ALLOC(newli, 8 + 4*oldli->no_keys - 4, 1); + newli->no_keys = oldli->no_keys - 1; no_keys = newli->no_keys; + newli->id = oldli->id; + + /* Now copy old, checking where to delete */ + for (o = 0, n = 0; o < oldli->no_keys; o++,n++) { + onkofs = oldli->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 > namlen) ? onk->len_name : namlen)) { + slot = o; + delnkofs = onkofs; delnk = onk; + rimax = rislot; + o++; + } + newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk; + } + + + } else { /* 'lf' or 'lh' are similar */ + +#ifdef DKDEBUG + printf("del_key: lf or lh handler\n"); +#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++; + } + + 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 */ + + } while (rislot < rimax); /* ri traverse loop */ + + if (slot == -1) { + printf("del_key: subkey %s not found!\n",name); + FREE(newlf); + FREE(newli); + return(1); + } + +#ifdef DKDEBUG + printf("del_key: key found at slot %d\n",slot); +#endif + + if (delnk->no_values || delnk->no_subkeys) { + printf("del_key: subkey %s has subkeys or values. Not deleted.\n",name); + FREE(newlf); + FREE(newli); + return(1); + } + + /* Allocate space for our new lf list and copy it into reg */ + if ( no_keys && (newlf || newli) ) { + newlfofs = alloc_block(hdesc, nkofs, 8 + (newlf ? 8 : 4) * no_keys); +#ifdef DKDEBUG + printf("del_key: alloc_block for index returns: %x\n",newlfofs); +#endif + if (!newlfofs) { + printf("del_key: WARNING: unable to allocate space for new key descriptor for %s! Not deleted\n",name); + 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); + */ + fill_block(hdesc, newlfofs, + ((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys); + + + } else { /* Last deleted, will throw away index */ + newlfofs = 0xfff; /* We subtract 0x1000 later */ + } + + if (newlfofs < 0xfff) { + printf("del_key: ERROR: newlfofs = %x\n",newlfofs); +#if DOCORE + debugit(hdesc->buffer,hdesc->size); + abort(); +#endif + } + + /* Check for CLASS data, if so, deallocate it too */ + if (delnk->len_classnam) { + free_block(hdesc, delnk->ofs_classnam + 0x1000); + } + /* Now it's safe to zap the nk */ + free_block(hdesc, delnkofs + 0x1000); + /* And the old index list */ + free_block(hdesc, (oldlfofs ? oldlfofs : oldliofs) + 0x1000); + + /* Update parent */ + key->no_subkeys--; + + 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); + + VERBF(hdesc,"del_key: need to delete ri-slot %d for %x - %s\n", rislot,nkofs,fullpath ); + + if (ri->no_lis > 1) { /* We have subindiceblocks left? */ + /* Delete from array */ + ALLOC(newri, 8 + 4*ri->no_lis - 4, 1); + newri->no_lis = ri->no_lis - 1; + newri->id = ri->id; + for (o = 0, n = 0; o < ri->no_lis; o++,n++) { + if (n == rislot) o++; + newri->hash[n].ofs_li = ri->hash[o].ofs_li; + } + newriofs = alloc_block(hdesc, nkofs, 8 + newri->no_lis*4 ); + if (!newriofs) { + printf("del_key: WARNING: unable to allocate space for ri-index for %s! Not deleted\n",name); + FREE(newlf); + 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; + FREE(newri); + } else { /* Last entry in ri was deleted, get rid of it, key is empty */ + VERB(hdesc,"del_key: .. and that was the last one. key now empty!\n"); + free_block(hdesc, riofs + 0x1000); + key->ofs_lf = -1; + } + } else { + ri->hash[rislot].ofs_li = newlfofs - 0x1000; + } + } else { + key->ofs_lf = newlfofs - 0x1000; + } + + FREE(newlf); + return(0); + +} + +/* Recursive delete keys + * hdesc - usual.. + * nkofs - offset of current nk + * name - name of key to delete + * return: 0 - ok, 1 fail + */ +void rdel_keys(struct hive *hdesc, char *path, int vofs) +{ + struct nk_key *key; + int nkofs; + struct ex_data ex; + int count = 0, countri = 0; + + + if (!path || !*path) return; + + nkofs = trav_path(hdesc, vofs, path, TPF_NK_EXACT); + + if(!nkofs) { + printf("rdel_keys: Key <%s> not found\n",path); + return; + } + nkofs += 4; + + key = (struct nk_key *)(hdesc->buffer + nkofs); + + /* + VERBF(hdesc,"rdel of node at offset 0x%0x\n",nkofs); + */ + + if (key->id != 0x6b6e) { + printf("Error: Not a 'nk' node!\n"); + + debugit(hdesc->buffer,hdesc->size); + + } + +#if 0 + printf("Node has %d subkeys and %d values\n",key->no_subkeys,key->no_values); +#endif + if (key->no_subkeys) { + while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) { +#if 0 + printf("%s\n",ex.name); +#endif + rdel_keys(hdesc, ex.name, nkofs); + count = 0; + countri = 0; + FREE(ex.name); + } + } + + del_allvalues(hdesc, nkofs); + key = (struct nk_key *)(hdesc->buffer + nkofs); + del_key(hdesc, key->ofs_parent+0x1004, path); + +} + + +/* Get and copy keys CLASS-data (if any) to buffer + * Returns a buffer with the data (first int32_t is size). see ntreg.h + * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice. + */ +struct keyval *get_class(struct hive *hdesc, + int curnk, char *path) +{ + int clen = 0, dofs = 0, nkofs; + struct nk_key *key; + struct keyval *data; + void *classdata; + + if (!path && !curnk) return(NULL); + + nkofs = trav_path(hdesc, curnk, path, 0); + + if(!nkofs) { + printf("get_class: Key <%s> not found\n",path); + return(NULL); + } + nkofs += 4; + key = (struct nk_key *)(hdesc->buffer + nkofs); + + clen = key->len_classnam; + if (!clen) { + printf("get_class: Key has no class data.\n"); + return(NULL); + } + + dofs = key->ofs_classnam; + classdata = (void *)(hdesc->buffer + dofs + 0x1004); + +#if 0 + printf("get_class: len_classnam = %d\n",clen); + printf("get_class: ofs_classnam = 0x%x\n",dofs); +#endif + + ALLOC(data, sizeof(struct keyval) + clen,1); + data->len = clen; + memcpy(&data->data, classdata, clen); + return(data); +} + + +/* Write to registry value. + * If same size as existing, copy back in place to avoid changing too much + * otherwise allocate new dataspace, then free the old + * Thus enough space to hold both new and old data is needed + * Pass inn buffer with data len as first DWORD (as routines above) + * returns: 0 - error, len - OK (len of data) + */ + +int put_buf2val(struct hive *hdesc, struct keyval *kv, + int vofs, char *path, int type, int exact ) +{ + int l; + 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); + return(0); + } + } + + keydataptr = get_val_data(hdesc, vofs, path, type, exact); + 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; + + /* 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); +} + +/* And, yer basic DWORD write */ + +int put_dword(struct hive *hdesc, int vofs, char *path, int exact, int dword) +{ + struct keyval *kr; + int r; + + ALLOC(kr,1,sizeof(int)+sizeof(int)); + + kr->len = sizeof(int); + kr->data = dword; + + r = put_buf2val(hdesc, kr, vofs, path, REG_DWORD, exact); + + FREE(kr); + + return(r); +} + +/* ================================================================ */ + +/* Code to export registry entries to .reg file initiated by + * Leo von Klenze + * Then expanded a bit to handle more types etc. + */ + +/* + * converts a value string from an registry entry into a c string. It does not + * use any encoding functions. + * It works very primitive by just taking every second char. + * The caller must free the resulting string, that was allocated with malloc. + * + * string: string where every second char is \0 + * len: length of the string + * return: the converted string as char* + */ +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; + char *cstring; + + int out_len = 0; + for(i = 0; i < len; ++i) + { + unsigned v = ((unsigned char *)string)[i]; + if (v < 128) + out_len += 1; + else + out_len += 2; + } + 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. + * + * hdesc: registry hive + * nkofs: offset of parent node + * name: name of key to export + * prefix: prefix for each key. This prefix is prepended to the keyname + * file: file descriptor of destination file + */ +void export_subkey(struct hive *hdesc, int nkofs, char *name, char *prefix, FILE *file) +{ + int newofs; + int count = 0; + int countri = 0; + int len; + char *path = (char*) malloc(1024); + char *value; + struct nk_key *key; + struct ex_data ex; + struct vex_data vex; + + + // switch to key + newofs = trav_path(hdesc, nkofs, name, TPF_NK_EXACT); + if(!newofs) + { + 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); + 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); + + // export the key + fprintf(file, "\r\n"); + fprintf(file, "[%s\%s]\r\n", prefix, path); + // export values + if(key->no_values) + { + 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, "dword:%08x\r\n", vex.val); + } + else if(vex.type == REG_SZ) + { + 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 + { + /* All other types seems to simply be hexdumped. Format is: + "valuename"=hex(typenum):xx,xx,xx,xx,xx... + for example: + "qword64"=hex(b):4e,03,51,db,fa,04,00,00 + this is type = 0xb = 11 = REG_QWORD + "expandstring"=hex(2):46,00,6f,00,6f,00,62,00,61,00,72,00,20,00,25,00,73,00,20,\ + 00,42,00,61,00,7a,00,00,00 + this is type 2 = REG_EXPAND_SZ and the line is continued with ,\ + don't know how many bytes for each line. Around 18-24 seems to be it?? depends on name lenght?? + NOTE: Exception: + type = REG_BINARY starts like this: "valuename"=hex:xx,xx,xx... + */ + 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); + + export_bin(vex.type, value, len, col, file); + } + + FREE(vex.name); + free(name); + } + } + + // export subkeys + if (key->no_subkeys) + { + count = 0; + countri = 0; + while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) + { + export_subkey(hdesc, nkofs, ex.name, prefix, file); + FREE(ex.name); + } + } + free(path); +} + +/* + * Exports the given key to a windows .reg file that can be imported to the + * windows registry. + * The prefix is used to determine the destination hive in the windows + * registry, set it to HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE or whatever you + * want. + * + * hdesc: hive + * nkofs: offset of parent node + * name: name of subkey to export + * filename: name of destination .reg file (will be overridden) + * prefix: prefix for each exported key + */ +void export_key(struct hive *hdesc, int nkofs, char *name, char *filename, char *prefix) +{ + FILE *file; + + // open file + file = fopen(filename, "w"); + if(!file) + { + 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"); + 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? */ + +} + + + + + + + + +/* ================================================================ */ + +/* Hive control (load/save/close) etc */ + +void closeHive(struct hive *hdesc) +{ + + // printf("closing hive %s\n",hdesc->filename); + if (hdesc->state & HMODE_OPEN) { + close(hdesc->filedesc); + } + FREE(hdesc->filename); + FREE(hdesc->buffer); + FREE(hdesc); + +} + + +/* 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); + + if ( !(hdesc->state & HMODE_OPEN)) { /* File has been closed */ + if (!(hdesc->filedesc = open(hdesc->filename,O_RDWR))) { + fprintf(stderr,"writeHive: open(%s) failed: %s, FILE NOT WRITTEN!\n",hdesc->filename,strerror(errno)); + return(1); + } + hdesc->state |= HMODE_OPEN; + } + /* 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)); + return(1); + } + + hdesc->state &= (~HMODE_DIRTY); + return(0); +} + +#undef LOAD_DEBUG + +struct hive *openHive(char *filename, int mode) +{ + + struct hive *hdesc; + int fmode,r,vofs; + struct stat sbuf; + uint32_t pofs; + 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) ? 1 : 0; + + CREATE(hdesc,struct hive,1); + + hdesc->filename = str_dup(filename); + hdesc->state = 0; + hdesc->size = 0; + hdesc->buffer = NULL; + + if ( (mode & HMODE_RO) ) { + fmode = O_RDONLY; + } else { + fmode = O_RDWR; + } + hdesc->filedesc = open(hdesc->filename,fmode); + if (hdesc->filedesc < 0) { + fprintf(stderr,"openHive(%s) failed: %s, trying read-only\n",hdesc->filename,strerror(errno)); + fmode = O_RDONLY; + mode |= HMODE_RO; + hdesc->filedesc = open(hdesc->filename,fmode); + if (hdesc->filedesc < 0) { + fprintf(stderr,"openHive(%s) in fallback RO-mode failed: %s\n",hdesc->filename,strerror(errno)); + closeHive(hdesc); + return(NULL); + } + } + + + if ( fstat(hdesc->filedesc,&sbuf) ) { + perror("stat()"); + exit(1); + } + + hdesc->size = sbuf.st_size; + hdesc->state = mode | HMODE_OPEN; + /* fprintf(stderr,"hiveOpen(%s) successful\n",hdesc->filename); */ + + /* Read the whole file */ + + ALLOC(hdesc->buffer,1,hdesc->size); + + r = read(hdesc->filedesc,hdesc->buffer,hdesc->size); + if (r < hdesc->size) { + fprintf(stderr,"Could not read file, got %d bytes while expecting %d\n", + r, hdesc->size); + closeHive(hdesc); + return(NULL); + } + + /* Now run through file, tallying all pages */ + /* NOTE/KLUDGE: Assume first page starts at offset 0x1000 */ + + pofs = 0x1000; + + hdr = (struct regf_header *)hdesc->buffer; + if (hdr->id != 0x66676572) { + 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); + + hdesc->rootofs = hdr->ofs_rootkey + 0x1000; + printf(">\nROOT KEY at offset: 0x%06x * ",hdesc->rootofs); + + /* Cache the roots subkey index type (li,lf,lh) so we can use the correct + * one when creating the first subkey in a key */ + + nk = (struct nk_key *)(hdesc->buffer + hdesc->rootofs + 4); + if (nk->id == 0x6b6e) { + rikey = (struct ri_key *)(hdesc->buffer + nk->ofs_lf + 0x1004); + hdesc->nkindextype = rikey->id; + if (hdesc->nkindextype == 0x6972) { /* Gee, big root, must check indirectly */ + printf("openHive: DEBUG: BIG ROOT!\n"); + rikey = (struct ri_key *)(hdesc->buffer + rikey->hash[0].ofs_li + 0x1004); + hdesc->nkindextype = rikey->id; + } + if (hdesc->nkindextype != 0x666c && + hdesc->nkindextype != 0x686c && + hdesc->nkindextype != 0x696c) { + hdesc->nkindextype = 0x666c; + } + + printf("Subkey indexing type is: %04x <%c%c>\n", + hdesc->nkindextype, + hdesc->nkindextype & 0xff, + hdesc->nkindextype >> 8); + } else { + fprintf(stderr,"openHive: WARNING: ROOT key does not seem to be a key! (not type == nk)\n"); + } + + + while (pofs < hdr->filesize + 0x1000) { /* Loop through hbins until end according to regf header */ +#ifdef LOAD_DEBUG + int htrace = 1; + // if (htrace) hexdump(hdesc->buffer,pofs,pofs+0x20,1); +#endif + p = (struct hbin_page *)(hdesc->buffer + pofs); + if (p->id != 0x6E696268) { + printf("Page at 0x%x is not 'hbin', assuming file contains garbage at end\n",pofs); + break; + } + hdesc->pages++; + + 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) { + fprintf(stderr,"openHive: ERROR: Page at 0x%x has size zero! File may be corrupt, or program has a bug\n",pofs); + return(hdesc); + } + + + 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); + + } + + 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); + + + /* 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); + +} + diff --git a/ntreg.h b/ntreg.h new file mode 100644 index 0000000..1012828 --- /dev/null +++ b/ntreg.h @@ -0,0 +1,444 @@ +/* + * ntreg.h - NT Registry Hive access library, constants & structures + * + * NOTE: defines are not frozen. It can and will change every release. + * + ***** + * + * NTREG - Window registry file reader / writer library + * 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 + * License as published by the Free Software Foundation; + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * See file LGPL.txt for the full license. + * + */ + + +#ifndef _INCLUDE_NTREG_H +#define _INCLUDE_NTREG_H 1 + +#define SZ_MAX 4096 /* Max unicode strlen before we truncate */ + +#define KEY_ROOT 0x2c /* Type ID of ROOT key node */ +#define KEY_NORMAL 0x20 /* Normal nk key */ + +#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 */ + +#define REG_NONE 0 /* No value type */ +#define REG_SZ 1 /* Unicode nul terminated string */ +#define REG_EXPAND_SZ 2 /* Unicode nul terminated string + env */ +#define REG_BINARY 3 /* Free form binary */ +#define REG_DWORD 4 /* 32-bit number */ +#define REG_DWORD_BIG_ENDIAN 5 /* 32-bit number */ +#define REG_LINK 6 /* Symbolic Link (unicode) */ +#define REG_MULTI_SZ 7 /* Multiple Unicode strings */ +#define REG_RESOURCE_LIST 8 /* Resource list in the resource map */ +#define REG_FULL_RESOURCE_DESCRIPTOR 9 /* Resource list in the hardware description */ +#define REG_RESOURCE_REQUIREMENTS_LIST 10 /* Uh? Rait.. */ +#define REG_QWORD 11 /* Quad word 64 bit, little endian */ + +#define REG_MAX 12 + + +/* The first page of the registry file is some kind of header, lot of + * it's contents is unknown, and seems to be mostly NULLs anyway. + * Note also, that this is the only place in the registry I've been + * able to find _any_ kind of checksumming + */ + +struct regf_header { + + int32_t id; /* 0x00000000 D-Word ID: ASCII-"regf" = 0x66676572 */ + 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 */ + 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 Offset of first non-used data at end of file */ + int32_t unknown7; /* 0x0000002C D-Word 1 */ + 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, + * they seem to be mostly NULLS + */ + +struct hbin_page { + + int32_t id; /* 0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268 */ + 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 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 + includes & NT docs. Currently, I have no use for it. + Note that keys sharing the exact same security settings will + most likely point to the same security descriptor, thus + saving space and making it fast to make objects inherit settings + (is inheritance supported? they speak of security inheritance as a "new" + feature in the filesystem on NT5, even though I think it was + also supported by the lower levels in the earlier versions) +*/ +struct sk_key { + + short id; /* 0x0000 Word ID: ASCII-"sk" = 0x6B73 */ + short dummy1; /* 0x0002 Word Unused */ + int32_t ofs_prevsk; /* 0x0004 D-Word Offset of previous "sk"-Record */ + int32_t ofs_nextsk; /* 0x0008 D-Word Offset of next "sk"-Record */ + int32_t no_usage; /* 0x000C D-Word usage-counter */ + int32_t len_sk; /* 0x0010 D-Word Size of "sk"-record in bytes */ + char data[4]; /* Security data up to len_sk bytes */ + +}; + +/* This is the subkeylist/hash structure. NT4.0+. + * ID + count, then count number of offset/4byte "hash". (not true hash) + * Probably changed from the 3.x version to make it faster to + * traverse the registry if you're looking for a specific name + * (saves lookups in 'nk's that have the first 4 name chars different) + */ + +struct lf_key { + + short id; /* 0x0000 Word ID: ASCII-"lf" = 0x666C or "lh" = 0x686c */ + short no_keys; /* 0x0002 Word number of keys */ + /* 0x0004 ???? Hash-Records */ + + union { + + struct lf_hash { + int32_t ofs_nk; /* 0x0000 D-Word Offset of corresponding "nk"-Record */ + char name[4]; /* 0x0004 D-Word ASCII: the first 4 characters of the key-name, */ + } hash[1]; + + /* WinXP uses a more real hash instead (base 37 of uppercase name chars) */ + /* padded with 0's. Case sensitiv! */ + + struct lh_hash { + int32_t ofs_nk; /* 0x0000 D-Word Offset of corresponding "nk"-Record */ + int32_t hash; /* 0x0004 D-Word ASCII: the first 4 characters of the key-name, */ + } lh_hash[1]; + }; + +}; + +/* 3.x version of the above, contains only offset table, NOT + * any start of names "hash". Thus needs 'nk' lookups for searches. + */ +struct li_key { + + short id; /* 0x0000 Word ID: ASCII-"li" = 0x696C */ + short no_keys; /* 0x0002 Word number of keys */ + /* 0x0004 ???? Hash-Records */ + struct li_hash { + int32_t ofs_nk; /* 0x0000 D-Word Offset of corresponding "nk"-Record */ + } hash[1]; +}; + + +/* 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 + * than about 400-500 entries/subkeys??, then the nk_key->ofs_lf points to this + * instead of directly to an lf. + * The sub-indices this points to seems to be li (yes!) in NT4 and 2k. + * In XP and newer they point to lh which is more efficient. + * Likely to happen in HKLM\Software\classes (file extention list) and + * in SAM when many users. + */ +struct ri_key { + + short id; /* 0x0000 Word ID: ASCII-"ri" = 0x6972 */ + short no_lis; /* 0x0002 Word number of pointers to li */ + /* 0x0004 ???? Hash-Records */ + struct ri_hash { + int32_t ofs_li; /* 0x0000 D-Word Offset of corresponding "li"-Record */ + } hash[1]; +}; + + +/* 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 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. + */ +struct vk_key { + + /* Offset Size Contents */ + short id; /* 0x0000 Word ID: ASCII-"vk" = 0x6B76 */ + short len_name; /* 0x0002 Word name length */ + 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 + 0x1 ANSI encoding */ + short dummy1; /* 0x0012 Word Unused (data-trash) */ + char keyname[1]; /* 0x0014 ???? Name */ + +}; + +/* This is the key node (ie directory) descriptor, can contain subkeys and/or values. + * Note that for values, the count is stored here, but for subkeys + * there's a count both here and in the offset-table (lf or li struct). + * What happens if these mismatch is not known. + * What's the classname thingy? Can't remember seeing that used in + * anything I've looked at. + */ +struct nk_key { + + /* 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 */ + int32_t dummy1; + int32_t ofs_lf; /* 0x001C D-Word Offset of the sub-key lf-Records */ + 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 */ + 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 */ +}; + +/*********************************************************************************/ + +/* Structure defines for my routines */ + +struct ex_data { + int nkoffs; + struct nk_key *nk; + char *name; +}; + +struct vex_data { + int vkoffs; + struct vk_key *vk; + int type; /* Value type REG_??? */ + int size; /* Values size (normalized, inline accounted for) */ + int val; /* Actual value itself if type==REG_DWORD */ + char *name; +}; + +struct keyval { + int len; /* Length of databuffer */ + int data; /* Data. Goes on for length of value */ +}; + +struct keyvala { + int len; /* Length of databuffer */ + int data[1]; /* Data. Goes on for length of value */ +}; + +/* 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 */ +#define HMODE_RW 0 +#define HMODE_RO 0x1 +#define HMODE_OPEN 0x2 +#define HMODE_DIRTY 0x4 +#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 + +/* Suggested type of hive loaded, guessed by library, but not used by it */ +#define HTYPE_UNKNOWN 0 +#define HTYPE_SAM 1 +#define HTYPE_SYSTEM 2 +#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 + */ +struct hive { + char *filename; /* Hives filename */ + int filedesc; /* File descriptor (only valid if state == OPEN) */ + int state; /* Current state of hive */ + int type; /* Suggested type of hive. NOTE: Library will guess when + it loads it, but application may change it if needed */ + int pages; /* Number of pages, total */ + int useblk; /* Total # of used blocks */ + int unuseblk; /* Total # of unused blocks */ + int usetot; /* total # of bytes in useblk */ + int unusetot; /* total # of bytes in unuseblk */ + 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 */ +}; + +/***************************************************/ + +/* Various nice macros */ + +#define CREATE(result, type, number)\ + { \ + if (!((result) = (type *) calloc ((number), sizeof(type)))) { \ + perror("malloc failure"); \ + abort() ; \ + } \ + } +#define ALLOC(result, size, number)\ + { \ + if (!((result) = (void *) calloc ((number), (size)))) { \ + perror("malloc failure"); \ + abort() ; \ + } \ + } +#define FREE(p) { if (p) { free(p); (p) = 0; } } + +/* Debug / verbosity message macro */ + +#define VERB(h, string) \ + { \ + if ((h)->state & HMODE_VERBOSE) printf((string)); \ + } + +#define VERBF(h, ...) \ + { \ + if ((h)->state & HMODE_VERBOSE) printf(__VA_ARGS__); \ + } + + +/******* Function prototypes **********/ + +char *str_dup( const char *str ); +int fmyinput(char *prmpt, char *ibuf, int maxlen); +void hexprnt(char *s, unsigned char *bytes, int len); +void hexdump(char *hbuf, int start, int stop, int ascii); +int find_in_buf(char *buf, char *what, int sz, int len, int start); +int get_int( char *array ); +void cheap_uni2ascii(char *src, char *dest, int l); +void cheap_ascii2uni(char *src, char *dest, int l); +void skipspace(char **c); +int gethex(char **c); +int gethexorstr(char **c, char *wb); +int debugit(char *buf, int sz); +int parse_block(struct hive *hdesc, int vofs,int verbose); +int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr); +int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr); +int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen); +int trav_path(struct hive *hdesc, int vofs, char *path, int type); +int get_val_type(struct hive *hdesc, int vofs, char *path, int exact); +int get_val_len(struct hive *hdesc, int vofs, char *path, int exact); +void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact); +struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv, + int vofs, char *path, int type, int exact ); +int get_dword(struct hive *hdesc, int vofs, char *path, int exact); +int put_buf2val(struct hive *hdesc, struct keyval *kv, + int vofs, char *path, int type, int exact ); +int put_dword(struct hive *hdesc, int vofs, char *path, int exact, int dword); +void export_key(struct hive *hdesc, int nkofs, char *name, char *filename, char *prefix); +void closeHive(struct hive *hdesc); +int writeHive(struct hive *hdesc); +struct hive *openHive(char *filename, int mode); + +void nk_ls(struct hive *hdesc, char *path, int vofs, int type); + +struct vk_key *add_value(struct hive *hdesc, int nkofs, char *name, int type); +void del_allvalues(struct hive *hdesc, int nkofs); +int del_value(struct hive *hdesc, int nkofs, char *name, int exact); +struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name); +int del_key(struct hive *hdesc, int nkofs, char *name); +void rdel_keys(struct hive *hdesc, char *path, int nkofs); +struct keyval *get_class(struct hive *hdesc, int curnk, char *path); + +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 --git a/reged.c b/reged.c new file mode 100644 index 0000000..b0d4a05 --- /dev/null +++ b/reged.c @@ -0,0 +1,233 @@ +/* + * reged.c - Simple Registry Edit Utility for Windows registry hives. + * + * 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.. + * + * + ***** + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See file GPL.txt for the full license. + * + ***** + */ + + +#include +#include +#include + + +#include "ntreg.h" + + +const char reged_version[] = "reged version 0.1 110511, (c) Petter N Hagen"; + + +/* Global verbosity flag */ +int gverbose = 0; + +/* Array of loaded hives */ +#define MAX_HIVES 10 +struct hive *hive[MAX_HIVES+1]; +int no_hives = 0; + + +void usage(void) +{ + 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\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" + ); +} + + +int main(int argc, char **argv) +{ + + int export = 0, edit = 0, import = 0; + int d = 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, *inputname; + char c; + char yn[10]; + FILE *ch; + + 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 && !import) { + usage(); + exit(1); + } + if ( import && export ) { + fprintf(stderr,"Import and export cannot be done at same time\n"); + usage(); + exit(1); + } + if (export) { /* Call export. Works only on one hive at a time */ + hivename=argv[optind]; + prefix=argv[optind+1]; + key=argv[optind+2]; + outputname=argv[optind+3]; + if (gverbose) { + printf("hivename: %s, prefix: %s, key: %s, output: %s\n",hivename,prefix,key,outputname); + } + + if (!hivename || !*hivename || !prefix || !*prefix || !key || !*key || !outputname || !*outputname) { + usage(); exit(1); + } + + if (!(hive[no_hives] = openHive(hivename,HMODE_RO|mode))) { + fprintf(stderr,"Unable to open/read hive %s, exiting..\n",hivename); + exit(1); + } + + export_key(hive[no_hives], 0, key, outputname, prefix); + + 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 && !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 && !autocommit) fmyinput("Commit changes to registry? (y/n) [n] : ",yn,3); + if (*yn == 'y' || logchange || autocommit) { + if (logchange) { + ch = fopen("/tmp/changed","w"); + } + for (il = 0; il < no_hives; il++) { + if (hive[il]->state & HMODE_DIRTY) { + printf("%2d <%s> - ",il,hive[il]->filename); + if (!writeHive(hive[il])) { + 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; + } + } + } + if (logchange) { + fprintf(ch,"\n"); + fclose(ch); + } + } else { + printf("Not written!\n\n"); + } + } else { + printf("None!\n\n"); + } + } + while (no_hives > 0) + closeHive(hive[--no_hives]); + return(dd); +} + diff --git a/regedit.txt b/regedit.txt new file mode 100644 index 0000000..a3b928d --- /dev/null +++ b/regedit.txt @@ -0,0 +1,438 @@ +The Offline NT Password Editor + +(c) 1997-2011 Petter Nordahl-Hagen + +Registry Editor Usermanual/docs + +See COPYING for copyright & credits. +See INSTALL for compile/installation instructions. +See README for docs on the passwordpart (or website for bootdisk) + +Some known limitations as of first half 2011: +This release features full basic registry edit with +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) + + +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) +Note that this demo is just some random editing, will likely +cause windows to not boot if written back to it. + +>chntpw -h +chntpw version 0.99.0 030111, (c) Petter N Hagen +chntpw: change password of a user in a NT SAM file, or invoke registry editor. +chntpw [OPTIONS] [systemfile] [securityfile] [otherreghive] [...] + -h This message + -u Username to change, Administrator is default + -l list all users in SAM file + -i Interactive. List users (as -l) then ask for username to change + -e Registry editor. Now with full write support! + -d Enter buffer debugger instead (hex editor), + -t Trace. Show hexdump of structs/segments. (deprecated debug function) + -L Write names of changed files to /tmp/changed + -N No allocation mode. Only (old style) same length overwrites possible + + (example edit of a SYSTEM-hive) +>chntpw -e system +chntpw version 0.99.0 030111, (c) Petter N Hagen +Hive's name (from header): +ROOT KEY at offset: 0x001020 +This is probably not a sam HIVE +Simple registry editor. ? for help. + +[1020] > ? + +Simple registry editor: +hive [] - list loaded hives or switch to hive numer n' +cd - change key +ls | dir [] - show subkeys & values, +cat | type - show key value +st [] - show struct info +nk - add key +dk - delete key (must be empty. recursion not supported yet) +ed - Edit value +nv - Add value +dv - Delete value +delallv - Delete all values in current key +debug - enter buffer hexeditor +q - quit + + (list the contents of the current key) + +[1020] > l +ls of node at offset 0x1024 +Node has 6 subkeys and 0 values +offs key name +[ 11b8] +[ dff88] +[1c2040] +[ de448] +[ de998] +[ deab8] + + (now let's make a subkey here) + +[1020] > nk DemoKey + +[1020] > l +ls of node at offset 0x1024 +Node has 7 subkeys and 0 values +offs key name +[ 11b8] +[ dff88] +[1c2ef8] +[1c2040] +[ de448] +[ de998] +[ deab8] + + (and delete the key) + +[1020] > dk DemoKey + +[1020] > l +ls of node at offset 0x1024 +Node has 6 subkeys and 0 values +offs key name +[ 11b8] +[ dff88] +[1c2040] +[ de448] +[ de998]