diff options
author | Richard Jones <rjones@redhat.com> | 2010-02-22 12:53:16 +0000 |
---|---|---|
committer | Richard Jones <rjones@redhat.com> | 2010-02-22 13:19:21 +0000 |
commit | a8c3723e3818b5a7a31520043c6831115ab9d0e0 (patch) | |
tree | 00568e6c18afe37e0b3963306fb19cade9b75f87 | |
parent | aa4700f0325743aaa16b525637b00ee61b39df13 (diff) | |
download | libguestfs-a8c3723e3818b5a7a31520043c6831115ab9d0e0.tar.gz libguestfs-a8c3723e3818b5a7a31520043c6831115ab9d0e0.tar.xz libguestfs-a8c3723e3818b5a7a31520043c6831115ab9d0e0.zip |
Spin off hivex as a separate upstream project.
This commit makes the semi-independent hivex library into a
separate upstream project. The git repo for hivex is now:
http://git.annexia.org/?p=hivex.git;a=summary
Downloads of hivex are available here:
http://libguestfs.org/download/
All questions, patches, bugs etc should be sent to the libguestfs
mailing list and bug tracker.
39 files changed, 20 insertions, 8143 deletions
@@ -80,18 +80,9 @@ haskell/Guestfs010Launch haskell/Guestfs050LVCreate haskell/Guestfs.hs *.hi -hivex/*.1 -hivex/*.3 -hivex/hivexsh -hivex/hivexml -hivex/tools/*.opt html/guestfish.1.html html/guestfs.3.html html/guestmount.1.html -html/hivex.3.html -html/hivexget.1.html -html/hivexsh.1.html -html/hivexml.1.html html/recipes.html html/virt-cat.1.html html/virt-df.1.html @@ -92,9 +92,10 @@ fuse/ haskell/ Haskell bindings. -hivex/ - Hive extraction library, for reading Windows Registry files. - See hivex/README for more details. +hivex/ [removed in 1.0.85] + This used to contain the hivex library for reading and + writing Windows Registry binary hive files. This is now + available as a separate upstream project. images/ Some guest images to test against. These are gzipped to save diff --git a/Makefile.am b/Makefile.am index 0abe3dd0..c1fc85d6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ include $(top_srcdir)/subdir-rules.mk ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = gnulib/lib hivex src daemon appliance fish po examples images \ +SUBDIRS = gnulib/lib src daemon appliance fish po examples images \ gnulib/tests capitests regressions test-tool # NB: Must build inspector directory after perl and before ocaml. @@ -118,10 +118,6 @@ HTMLFILES = \ html/guestfs.3.html \ html/guestfish.1.html \ html/guestmount.1.html \ - html/hivex.3.html \ - html/hivexget.1.html \ - html/hivexml.1.html \ - html/hivexsh.1.html \ html/virt-cat.1.html \ html/virt-df.1.html \ html/virt-edit.1.html \ @@ -52,7 +52,7 @@ Requirements - genisoimage / mkisofs -- libxml2 +- (Optional) hivex to build Windows Registry support - (Optional) FUSE to build the FUSE module diff --git a/configure.ac b/configure.ac index 1ba449e5..744d24a6 100644 --- a/configure.ac +++ b/configure.ac @@ -422,10 +422,16 @@ dnl For i18n. AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.17]) -dnl libxml2 is used by the hivex library. -PKG_CHECK_MODULES([LIBXML2], [libxml-2.0]) -AC_SUBST([LIBXML2_CFLAGS]) -AC_SUBST([LIBXML2_LIBS]) +dnl hivex library (highly recommended). +dnl This used to be a part of libguestfs, but was spun off into its +dnl own separate upstream project in libguestfs 1.0.85. +HAVE_HIVEX=yes +PKG_CHECK_MODULES([HIVEX], [hivex],,[ + HAVE_HIVEX=no + AC_MSG_WARN([Hivex library and headers are missing, so optional Windows Registry tools won't be built])]) +AM_CONDITIONAL([HAVE_HIVEX],[test "x$HAVE_HIVEX" = "xyes"]) +AC_SUBST([HIVEX_CFLAGS]) +AC_SUBST([HIVEX_LIBS]) dnl FUSE is optional to build the FUSE module. HAVE_FUSE=yes @@ -736,9 +742,6 @@ AC_CONFIG_FILES([Makefile libguestfs.pc gnulib/lib/Makefile gnulib/tests/Makefile - hivex/Makefile - hivex/t/Makefile - hivex/tools/Makefile fuse/Makefile ocaml/META perl/Makefile.PL]) AC_OUTPUT diff --git a/hivex/LICENSE b/hivex/LICENSE deleted file mode 100644 index 38dec6da..00000000 --- a/hivex/LICENSE +++ /dev/null @@ -1,506 +0,0 @@ -This is the license for the hivex library. - ----------------------------------------------------------------------- - - 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. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - 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. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/hivex/Makefile.am b/hivex/Makefile.am deleted file mode 100644 index 66c5bf1d..00000000 --- a/hivex/Makefile.am +++ /dev/null @@ -1,141 +0,0 @@ -# libguestfs -# Copyright (C) 2009 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -SUBDIRS = t tools - -EXTRA_DIST = \ - hivex.pod \ - hivexml.pod \ - hivexget.pod \ - hivexget \ - hivexsh.pod \ - LICENSE \ - example1 \ - example2 \ - example3 \ - example4 \ - example5 \ - example6 - -lib_LTLIBRARIES = libhivex.la - -libhivex_la_SOURCES = \ - hivex.c \ - hivex.h \ - byte_conversions.h - -libhivex_la_LDFLAGS = -version-info 0:0:0 $(LTLIBINTL) $(LTLIBTHREAD) -libhivex_la_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) -libhivex_la_CPPFLAGS = -I$(top_srcdir)/gnulib/lib - -bin_PROGRAMS = hivexml hivexsh -bin_SCRIPTS = hivexget -noinst_SCRIPTS = example1 example2 example3 example4 example5 example6 - -hivexml_SOURCES = \ - hivexml.c - -hivexml_LDADD = libhivex.la $(LIBXML2_LIBS) ../gnulib/lib/libgnu.la -hivexml_CFLAGS = \ - -I$(top_srcdir)/src \ - -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ - $(LIBXML2_CFLAGS) \ - $(WARN_CFLAGS) $(WERROR_CFLAGS) - -hivexsh_SOURCES = \ - hivexsh.c \ - hivex.h \ - byte_conversions.h - -hivexsh_LDADD = libhivex.la ../gnulib/lib/libgnu.la $(LIBREADLINE) -hivexsh_CFLAGS = \ - -I$(top_srcdir)/gnulib/lib \ - -I$(top_srcdir)/src \ - -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ - $(WARN_CFLAGS) $(WERROR_CFLAGS) - -man_MANS = hivex.3 hivexml.1 hivexget.1 hivexsh.1 - -hivex.3: hivex.pod - $(POD2MAN) \ - --section 3 \ - -c "Windows Registry" \ - --name "hivex" \ - --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ - $< > $@-t; mv $@-t $@ - -hivexml.1: hivexml.pod - $(POD2MAN) \ - --section 1 \ - -c "Windows Registry" \ - --name "hivexml" \ - --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ - $< > $@-t; mv $@-t $@ - -hivexget.1: hivexget.pod - $(POD2MAN) \ - --section 1 \ - -c "Windows Registry" \ - --name "hivexget" \ - --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ - $< > $@-t; mv $@-t $@ - -hivexsh.1: hivexsh.pod - $(POD2MAN) \ - --section 1 \ - -c "Windows Registry" \ - --name "hivexsh" \ - --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ - $< > $@-t; mv $@-t $@ - -noinst_DATA = \ - $(top_builddir)/html/hivex.3.html \ - $(top_builddir)/html/hivexml.1.html \ - $(top_builddir)/html/hivexget.1.html \ - $(top_builddir)/html/hivexsh.1.html - -$(top_builddir)/html/hivex.3.html: hivex.pod - mkdir -p $(top_builddir)/html - cd $(top_builddir) && pod2html \ - --css 'pod.css' \ - --htmldir html \ - --outfile html/hivex.3.html \ - hivex/hivex.pod - -$(top_builddir)/html/hivexml.1.html: hivexml.pod - mkdir -p $(top_builddir)/html - cd $(top_builddir) && pod2html \ - --css 'pod.css' \ - --htmldir html \ - --outfile html/hivexml.1.html \ - hivex/hivexml.pod - -$(top_builddir)/html/hivexget.1.html: hivexget.pod - mkdir -p $(top_builddir)/html - cd $(top_builddir) && pod2html \ - --css 'pod.css' \ - --htmldir html \ - --outfile html/hivexget.1.html \ - hivex/hivexget.pod - -$(top_builddir)/html/hivexsh.1.html: hivexsh.pod - mkdir -p $(top_builddir)/html - cd $(top_builddir) && pod2html \ - --css 'pod.css' \ - --htmldir html \ - --outfile html/hivexsh.1.html \ - hivex/hivexsh.pod diff --git a/hivex/README b/hivex/README deleted file mode 100644 index 3f7f0188..00000000 --- a/hivex/README +++ /dev/null @@ -1,35 +0,0 @@ -hivex - by Richard W.M. Jones, rjones@redhat.com -Copyright (C) 2009-2010 Red Hat Inc. ----------------------------------------------------------------------- - -This is a self-contained library for reading Windows Registry "hive" -binary files. - -Unlike many other tools in this area, it doesn't use the textual .REG -format for output, because parsing that is as much trouble as parsing -the original binary format. Instead it makes the file available -through a C API, or there is a separate program to export the hive as -XML. - -This library was derived from several sources: - - . NTREG registry reader/writer library by Petter Nordahl-Hagen - (LGPL v2.1 licensed library and program) - . http://pogostick.net/~pnh/ntpasswd/WinReg.txt - . dumphive (a BSD-licensed Pascal program by Markus Stephany) - . http://www.sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf - . editreg program from Samba - this program was removed in later - versions of Samba, so you have to go back in the source repository - to find it (GPLv2+) - . http://amnesia.gtisc.gatech.edu/~moyix/suzibandit.ltd.uk/MSc/ - . reverse engineering the format (see hivex/tools/visualizer.ml) - -Like NTREG, this library only attempts to read Windows NT registry -files (ie. not Windows 3.1 or Windows 95/98/ME). See the link above -for documentation on the older formats if you wish to read them. - -Unlike NTREG, this code is much more careful about handling error -cases, corrupt and malicious registry files, and endianness. - -The license for this library is LGPL v2.1, but not later versions. -For full details, see the file LICENSE in this directory. diff --git a/hivex/byte_conversions.h b/hivex/byte_conversions.h deleted file mode 100644 index 84e9e2de..00000000 --- a/hivex/byte_conversions.h +++ /dev/null @@ -1,89 +0,0 @@ -/* Useful byte conversion macros, not available on all platforms. - * Copyright (C) 2009-2010 Red Hat Inc. - * - * 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. - */ - -#ifndef hivex_byteorder_h -#define hivex_byteorder_h - -#ifdef HAVE_ENDIAN_H -#include <endian.h> -#endif -#ifdef HAVE_BYTESWAP_H -#include <byteswap.h> -#endif - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#ifndef be32toh -#define be32toh(x) __bswap_32 (x) -#endif -#ifndef htobe32 -#define htobe32(x) __bswap_32 (x) -#endif -#ifndef be64toh -#define be64toh(x) __bswap_64 (x) -#endif -#ifndef htobe64 -#define htobe64(x) __bswap_64 (x) -#endif -#ifndef le16toh -#define le16toh(x) (x) -#endif -#ifndef htole16 -#define htole16(x) (x) -#endif -#ifndef le32toh -#define le32toh(x) (x) -#endif -#ifndef htole32 -#define htole32(x) (x) -#endif -#ifndef le64toh -#define le64toh(x) (x) -#endif -#ifndef htole64 -#define htole64(x) (x) -#endif -#else /* __BYTE_ORDER == __BIG_ENDIAN */ -#ifndef be32toh -#define be32toh(x) (x) -#endif -#ifndef htobe32 -#define htobe32(x) (x) -#endif -#ifndef be64toh -#define be64toh(x) (x) -#endif -#ifndef htobe64 -#define htobe64(x) (x) -#endif -#ifndef le16toh -#define le16toh(x) __bswap_16 (x) -#endif -#ifndef htole16 -#define htole16(x) __bswap_16 (x) -#endif -#ifndef le32toh -#define le32toh(x) __bswap_32 (x) -#endif -#ifndef htole32 -#define htole32(x) __bswap_32 (x) -#endif -#ifndef le64toh -#define le64toh(x) __bswap_64 (x) -#endif -#ifndef htole64 -#define htole64(x) __bswap_64 (x) -#endif -#endif /* __BYTE_ORDER == __BIG_ENDIAN */ - -#endif /* hivex_byteorder_h */ diff --git a/hivex/example1 b/hivex/example1 deleted file mode 100755 index 5b1313f8..00000000 --- a/hivex/example1 +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -# Example program which loads and saves a hive. -# -# The intention of this example is just to check that we can do this -# without corrupting the hive (header etc). -# -# NB: The copy of the hive will not be absolutely identical. The -# sequence numbers in the header will change. If we implement the -# last modified field in the header, then that and the checksum will -# also change. - -if [ $# -ne 2 ]; then - echo "$0 input output" - exit 1 -fi - -d=`dirname $0` - -$d/hivexsh -w <<EOF -load $1 -commit $2 -EOF
\ No newline at end of file diff --git a/hivex/example2 b/hivex/example2 deleted file mode 100755 index 8d275463..00000000 --- a/hivex/example2 +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -# Example program which modifies a hive. -# -# This program removes any existing (key, value) pairs at the root -# node and replaces them with some example values. -# -# You can load the modified hive using another tool to see the -# changes. eg. Using Windows regedit, select HKLM and then in the -# File menu choose "Load Hive ...". Point to the update hive, and -# then give a key (eg. "test1"). The modified hive will be loaded -# under HKLM\test1 and the values can be inspected there. After -# inspecting the changes, unload the hive using File -> Unload Hive. -# -# Don't replace the original Windows hive, else you'll break things :-) - -if [ $# -ne 0 ]; then - echo "$0: no arguments required" - exit 1 -fi - -d=`dirname $0` - -$d/hivexsh -w <<EOF -load $d/t/minimal -setval 1 -@ -string:Root -commit /tmp/modified -EOF
\ No newline at end of file diff --git a/hivex/example3 b/hivex/example3 deleted file mode 100755 index b482e410..00000000 --- a/hivex/example3 +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -# Example program which modifies a hive. -# -# This program removes any existing (key, value) pairs at the root -# node and replaces them with some example values. -# -# You can load the modified hive using another tool to see the -# changes. eg. Using Windows regedit, select HKLM and then in the -# File menu choose "Load Hive ...". Point to the update hive, and -# then give a key (eg. "test1"). The modified hive will be loaded -# under HKLM\test1 and the values can be inspected there. After -# inspecting the changes, unload the hive using File -> Unload Hive. -# -# Don't replace the original Windows hive, else you'll break things :-) - -if [ $# -ne 0 ]; then - echo "$0: no arguments required" - exit 1 -fi - -d=`dirname $0` - -$d/hivexsh -w <<EOF -load $d/t/minimal -setval 4 -@ -string:Root -A -string:abcd -B -dword:0x12345678 -C -string:dcbadcbadcbaabcd -commit /tmp/modified -EOF
\ No newline at end of file diff --git a/hivex/example4 b/hivex/example4 deleted file mode 100755 index 85fd5526..00000000 --- a/hivex/example4 +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -# This program deletes the whole \Microsoft tree from a software hive. - -if [ $# -ne 2 ]; then - echo "$0 software software.new" - exit 1 -fi - -d=`dirname $0` - -$d/hivexsh -w <<EOF -load $1 -cd \Microsoft -del -commit $2 -EOF
\ No newline at end of file diff --git a/hivex/example5 b/hivex/example5 deleted file mode 100755 index ccf711ca..00000000 --- a/hivex/example5 +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -# This script adds a new node under \Microsoft in an existing software -# hive. - -if [ $# -ne 2 ]; then - echo "$0 software software.new" - exit 1 -fi - -d=`dirname $0` - -$d/hivexsh -w <<EOF -load $1 -cd \Microsoft -add TestNode -cd TestNode -add Test1 -add Test2 -add Test3 -add Test4 -add Test5 -cd Test1 -setval 2 -@ -string:This is the default key of Test1 -ThisIsTest1 -dword:0x12345678 -cd .. -cd Test5 -setval 2 -@ -string:This is the default key of Test5 -ThisIsTest5 -dword:0x87654321 -commit $2 -EOF
\ No newline at end of file diff --git a/hivex/example6 b/hivex/example6 deleted file mode 100755 index 7cb44675..00000000 --- a/hivex/example6 +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -# Hypothetical viostor installation in a W2K3 system registry. - -if [ $# -ne 2 ]; then - echo "$0 system system.new" - exit 1 -fi - -d=`dirname $0` - -$d/hivexsh -w <<EOF -load $1 - -cd \ControlSet001\Control\CriticalDeviceDatabase - -add pci#ven_1af4&dev_1001&subsys_00000000 -cd pci#ven_1af4&dev_1001&subsys_00000000 -setval 2 -Service -string:viostor -ClassGUID -string:{4D36E97B-E325-11CE-BFC1-08002BE10318} -cd .. - -add pci#ven_1af4&dev_1001&subsys_00020000 -cd pci#ven_1af4&dev_1001&subsys_00020000 -setval 2 -Service -string:viostor -ClassGUID -string:{4D36E97B-E325-11CE-BFC1-08002BE10318} -cd .. - -add pci#ven_1af4&dev_1001&subsys_00021af4 -cd pci#ven_1af4&dev_1001&subsys_00021af4 -setval 2 -Service -string:viostor -ClassGUID -string:{4D36E97B-E325-11CE-BFC1-08002BE10318} - -cd \ControlSet001\Services -add viostor -cd viostor -setval 6 -Type -dword:0x00000001 -Start -dword:0x00000000 -Group -string:SCSI miniport -ErrorControl -dword:0x00000001 -ImagePath -string:system32\drivers\viostor.sys -Tag -dword:0x00000021 - -add Parameters -cd Parameters -setval 1 -BusType -dword:0x00000001 - -add MaxTransferSize -cd MaxTransferSize -setval 3 -ParamDesc -string:Maximum Transfer Size -type -string:enum -default -string:0 - -add enum -cd enum -setval 3 -0 -string:64 KB -1 -string:128 KB -2 -string:256 KB -cd .. - -cd .. - -add PnpInterface -cd PnpInterface -setval 1 -5 -dword:0x00000001 -cd .. - -cd .. - -add Enum -cd Enum -setval 3 -0 -string:PCI\VEN_1AF4&DEV_1001&SUBSYS_00021AF4&REV_00\3&13c0b0c5&0&20 -Count -dword:0x00000001 -NextInstance -dword:0x00000001 - -commit $2 -EOF diff --git a/hivex/hivex.c b/hivex/hivex.c deleted file mode 100644 index 7d26eebd..00000000 --- a/hivex/hivex.c +++ /dev/null @@ -1,2557 +0,0 @@ -/* hivex - Windows Registry "hive" extraction library. - * Copyright (C) 2009-2010 Red Hat Inc. - * Derived from code by Petter Nordahl-Hagen under a compatible license: - * Copyright (c) 1997-2007 Petter Nordahl-Hagen. - * Derived from code by Markus Stephany under a compatible license: - * Copyright (c) 2000-2004, Markus Stephany. - * - * 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 LICENSE for the full license. - */ - -#include <config.h> - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <stddef.h> -#include <inttypes.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <iconv.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <assert.h> - -#include "c-ctype.h" -#include "full-read.h" -#include "full-write.h" - -#ifndef O_CLOEXEC -#define O_CLOEXEC 0 -#endif - -#define STREQ(a,b) (strcmp((a),(b)) == 0) -#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) -//#define STRNEQ(a,b) (strcmp((a),(b)) != 0) -//#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) -#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) -//#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) -//#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) -//#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) -#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0) - -#include "hivex.h" -#include "byte_conversions.h" - -/* These limits are in place to stop really stupid stuff and/or exploits. */ -#define HIVEX_MAX_SUBKEYS 10000 -#define HIVEX_MAX_VALUES 1000 -#define HIVEX_MAX_VALUE_LEN 1000000 -#define HIVEX_MAX_ALLOCATION 1000000 - -static char *windows_utf16_to_utf8 (/* const */ char *input, size_t len); - -struct hive_h { - char *filename; - int fd; - size_t size; - int msglvl; - int writable; - - /* Registry file, memory mapped if read-only, or malloc'd if writing. */ - union { - char *addr; - struct ntreg_header *hdr; - }; - - /* Use a bitmap to store which file offsets are valid (point to a - * used block). We only need to store 1 bit per 32 bits of the file - * (because blocks are 4-byte aligned). We found that the average - * block size in a registry file is ~50 bytes. So roughly 1 in 12 - * bits in the bitmap will be set, making it likely a more efficient - * structure than a hash table. - */ - char *bitmap; -#define BITMAP_SET(bitmap,off) (bitmap[(off)>>5] |= 1 << (((off)>>2)&7)) -#define BITMAP_CLR(bitmap,off) (bitmap[(off)>>5] &= ~ (1 << (((off)>>2)&7))) -#define BITMAP_TST(bitmap,off) (bitmap[(off)>>5] & (1 << (((off)>>2)&7))) -#define IS_VALID_BLOCK(h,off) \ - (((off) & 3) == 0 && \ - (off) >= 0x1000 && \ - (off) < (h)->size && \ - BITMAP_TST((h)->bitmap,(off))) - - /* Fields from the header, extracted from little-endianness hell. */ - size_t rootoffs; /* Root key offset (always an nk-block). */ - size_t endpages; /* Offset of end of pages. */ - - /* For writing. */ - size_t endblocks; /* Offset to next block allocation (0 - if not allocated anything yet). */ -}; - -/* NB. All fields are little endian. */ -struct ntreg_header { - char magic[4]; /* "regf" */ - uint32_t sequence1; - uint32_t sequence2; - char last_modified[8]; - uint32_t major_ver; /* 1 */ - uint32_t minor_ver; /* 3 */ - uint32_t unknown5; /* 0 */ - uint32_t unknown6; /* 1 */ - uint32_t offset; /* offset of root key record - 4KB */ - uint32_t blocks; /* pointer AFTER last hbin in file - 4KB */ - uint32_t unknown7; /* 1 */ - /* 0x30 */ - char name[64]; /* original file name of hive */ - char unknown_guid1[16]; - char unknown_guid2[16]; - /* 0x90 */ - uint32_t unknown8; - char unknown_guid3[16]; - uint32_t unknown9; - /* 0xa8 */ - char unknown10[340]; - /* 0x1fc */ - uint32_t csum; /* checksum: xor of dwords 0-0x1fb. */ - /* 0x200 */ - char unknown11[3528]; - /* 0xfc8 */ - char unknown_guid4[16]; - char unknown_guid5[16]; - char unknown_guid6[16]; - uint32_t unknown12; - uint32_t unknown13; - /* 0x1000 */ -} __attribute__((__packed__)); - -struct ntreg_hbin_page { - char magic[4]; /* "hbin" */ - uint32_t offset_first; /* offset from 1st block */ - uint32_t page_size; /* size of this page (multiple of 4KB) */ - char unknown[20]; - /* Linked list of blocks follows here. */ -} __attribute__((__packed__)); - -struct ntreg_hbin_block { - int32_t seg_len; /* length of this block (-ve for used block) */ - char id[2]; /* the block type (eg. "nk" for nk record) */ - /* Block data follows here. */ -} __attribute__((__packed__)); - -#define BLOCK_ID_EQ(h,offs,eqid) \ - (STREQLEN (((struct ntreg_hbin_block *)((h)->addr + (offs)))->id, (eqid), 2)) - -static size_t -block_len (hive_h *h, size_t blkoff, int *used) -{ - struct ntreg_hbin_block *block; - block = (struct ntreg_hbin_block *) (h->addr + blkoff); - - int32_t len = le32toh (block->seg_len); - if (len < 0) { - if (used) *used = 1; - len = -len; - } else { - if (used) *used = 0; - } - - return (size_t) len; -} - -struct ntreg_nk_record { - int32_t seg_len; /* length (always -ve because used) */ - char id[2]; /* "nk" */ - uint16_t flags; - char timestamp[8]; - uint32_t unknown1; - uint32_t parent; /* offset of owner/parent */ - uint32_t nr_subkeys; /* number of subkeys */ - uint32_t nr_subkeys_volatile; - uint32_t subkey_lf; /* lf record containing list of subkeys */ - uint32_t subkey_lf_volatile; - uint32_t nr_values; /* number of values */ - uint32_t vallist; /* value-list record */ - uint32_t sk; /* offset of sk-record */ - uint32_t classname; /* offset of classname record */ - uint16_t max_subkey_name_len; /* maximum length of a subkey name in bytes - if the subkey was reencoded as UTF-16LE */ - uint16_t unknown2; - uint32_t unknown3; - uint32_t max_vk_name_len; /* maximum length of any vk name in bytes - if the name was reencoded as UTF-16LE */ - uint32_t max_vk_data_len; /* maximum length of any vk data in bytes */ - uint32_t unknown6; - uint16_t name_len; /* length of name */ - uint16_t classname_len; /* length of classname */ - char name[1]; /* name follows here */ -} __attribute__((__packed__)); - -struct ntreg_lf_record { - int32_t seg_len; - char id[2]; /* "lf"|"lh" */ - uint16_t nr_keys; /* number of keys in this record */ - struct { - uint32_t offset; /* offset of nk-record for this subkey */ - char hash[4]; /* hash of subkey name */ - } keys[1]; -} __attribute__((__packed__)); - -struct ntreg_ri_record { - int32_t seg_len; - char id[2]; /* "ri" */ - uint16_t nr_offsets; /* number of pointers to lh records */ - uint32_t offset[1]; /* list of pointers to lh records */ -} __attribute__((__packed__)); - -/* This has no ID header. */ -struct ntreg_value_list { - int32_t seg_len; - uint32_t offset[1]; /* list of pointers to vk records */ -} __attribute__((__packed__)); - -struct ntreg_vk_record { - int32_t seg_len; /* length (always -ve because used) */ - char id[2]; /* "vk" */ - uint16_t name_len; /* length of name */ - /* length of the data: - * If data_len is <= 4, then it's stored inline. - * Top bit is set to indicate inline. - */ - uint32_t data_len; - uint32_t data_offset; /* pointer to the data (or data if inline) */ - uint32_t data_type; /* type of the data */ - uint16_t flags; /* bit 0 set => key name ASCII, - bit 0 clr => key name UTF-16. - Only seen ASCII here in the wild. - NB: this is CLEAR for default key. */ - uint16_t unknown2; - char name[1]; /* key name follows here */ -} __attribute__((__packed__)); - -struct ntreg_sk_record { - int32_t seg_len; /* length (always -ve because used) */ - char id[2]; /* "sk" */ - uint16_t unknown1; - uint32_t sk_next; /* linked into a circular list */ - uint32_t sk_prev; - uint32_t refcount; /* reference count */ - uint32_t sec_len; /* length of security info */ - char sec_desc[1]; /* security info follows */ -} __attribute__((__packed__)); - -static uint32_t -header_checksum (const hive_h *h) -{ - uint32_t *daddr = (uint32_t *) h->addr; - size_t i; - uint32_t sum = 0; - - for (i = 0; i < 0x1fc / 4; ++i) { - sum ^= le32toh (*daddr); - daddr++; - } - - return sum; -} - -hive_h * -hivex_open (const char *filename, int flags) -{ - hive_h *h = NULL; - - assert (sizeof (struct ntreg_header) == 0x1000); - assert (offsetof (struct ntreg_header, csum) == 0x1fc); - - h = calloc (1, sizeof *h); - if (h == NULL) - goto error; - - h->msglvl = flags & HIVEX_OPEN_MSGLVL_MASK; - - const char *debug = getenv ("HIVEX_DEBUG"); - if (debug && STREQ (debug, "1")) - h->msglvl = 2; - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_open: created handle %p\n", h); - - h->writable = !!(flags & HIVEX_OPEN_WRITE); - h->filename = strdup (filename); - if (h->filename == NULL) - goto error; - - h->fd = open (filename, O_RDONLY | O_CLOEXEC); - if (h->fd == -1) - goto error; - - struct stat statbuf; - if (fstat (h->fd, &statbuf) == -1) - goto error; - - h->size = statbuf.st_size; - - if (!h->writable) { - h->addr = mmap (NULL, h->size, PROT_READ, MAP_SHARED, h->fd, 0); - if (h->addr == MAP_FAILED) - goto error; - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_open: mapped file at %p\n", h->addr); - } else { - h->addr = malloc (h->size); - if (h->addr == NULL) - goto error; - - if (full_read (h->fd, h->addr, h->size) < h->size) - goto error; - } - - /* Check header. */ - if (h->hdr->magic[0] != 'r' || - h->hdr->magic[1] != 'e' || - h->hdr->magic[2] != 'g' || - h->hdr->magic[3] != 'f') { - fprintf (stderr, "hivex: %s: not a Windows NT Registry hive file\n", - filename); - errno = ENOTSUP; - goto error; - } - - /* Check major version. */ - uint32_t major_ver = le32toh (h->hdr->major_ver); - if (major_ver != 1) { - fprintf (stderr, - "hivex: %s: hive file major version %" PRIu32 " (expected 1)\n", - filename, major_ver); - errno = ENOTSUP; - goto error; - } - - h->bitmap = calloc (1 + h->size / 32, 1); - if (h->bitmap == NULL) - goto error; - - /* Header checksum. */ - uint32_t sum = header_checksum (h); - if (sum != le32toh (h->hdr->csum)) { - fprintf (stderr, "hivex: %s: bad checksum in hive header\n", filename); - errno = EINVAL; - goto error; - } - - if (h->msglvl >= 2) { - char *name = windows_utf16_to_utf8 (h->hdr->name, 64); - - fprintf (stderr, - "hivex_open: header fields:\n" - " file version %" PRIu32 ".%" PRIu32 "\n" - " sequence nos %" PRIu32 " %" PRIu32 "\n" - " (sequences nos should match if hive was synched at shutdown)\n" - " original file name %s\n" - " (only 32 chars are stored, name is probably truncated)\n" - " root offset 0x%x + 0x1000\n" - " end of last page 0x%x + 0x1000 (total file size 0x%zx)\n" - " checksum 0x%x (calculated 0x%x)\n", - major_ver, le32toh (h->hdr->minor_ver), - le32toh (h->hdr->sequence1), le32toh (h->hdr->sequence2), - name ? name : "(conversion failed)", - le32toh (h->hdr->offset), - le32toh (h->hdr->blocks), h->size, - le32toh (h->hdr->csum), sum); - free (name); - } - - h->rootoffs = le32toh (h->hdr->offset) + 0x1000; - h->endpages = le32toh (h->hdr->blocks) + 0x1000; - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_open: root offset = 0x%zx\n", h->rootoffs); - - /* We'll set this flag when we see a block with the root offset (ie. - * the root block). - */ - int seen_root_block = 0, bad_root_block = 0; - - /* Collect some stats. */ - size_t pages = 0; /* Number of hbin pages read. */ - size_t smallest_page = SIZE_MAX, largest_page = 0; - size_t blocks = 0; /* Total number of blocks found. */ - size_t smallest_block = SIZE_MAX, largest_block = 0, blocks_bytes = 0; - size_t used_blocks = 0; /* Total number of used blocks found. */ - size_t used_size = 0; /* Total size (bytes) of used blocks. */ - - /* Read the pages and blocks. The aim here is to be robust against - * corrupt or malicious registries. So we make sure the loops - * always make forward progress. We add the address of each block - * we read to a hash table so pointers will only reference the start - * of valid blocks. - */ - size_t off; - struct ntreg_hbin_page *page; - for (off = 0x1000; off < h->size; off += le32toh (page->page_size)) { - if (off >= h->endpages) - break; - - page = (struct ntreg_hbin_page *) (h->addr + off); - if (page->magic[0] != 'h' || - page->magic[1] != 'b' || - page->magic[2] != 'i' || - page->magic[3] != 'n') { - fprintf (stderr, "hivex: %s: trailing garbage at end of file (at 0x%zx, after %zu pages)\n", - filename, off, pages); - errno = ENOTSUP; - goto error; - } - - size_t page_size = le32toh (page->page_size); - if (h->msglvl >= 2) - fprintf (stderr, "hivex_open: page at 0x%zx, size %zu\n", off, page_size); - pages++; - if (page_size < smallest_page) smallest_page = page_size; - if (page_size > largest_page) largest_page = page_size; - - if (page_size <= sizeof (struct ntreg_hbin_page) || - (page_size & 0x0fff) != 0) { - fprintf (stderr, "hivex: %s: page size %zu at 0x%zx, bad registry\n", - filename, page_size, off); - errno = ENOTSUP; - goto error; - } - - /* Read the blocks in this page. */ - size_t blkoff; - struct ntreg_hbin_block *block; - size_t seg_len; - for (blkoff = off + 0x20; - blkoff < off + page_size; - blkoff += seg_len) { - blocks++; - - int is_root = blkoff == h->rootoffs; - if (is_root) - seen_root_block = 1; - - block = (struct ntreg_hbin_block *) (h->addr + blkoff); - int used; - seg_len = block_len (h, blkoff, &used); - if (seg_len <= 4 || (seg_len & 3) != 0) { - fprintf (stderr, "hivex: %s: block size %" PRIu32 " at 0x%zx, bad registry\n", - filename, le32toh (block->seg_len), blkoff); - errno = ENOTSUP; - goto error; - } - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_open: %s block id %d,%d at 0x%zx size %zu%s\n", - used ? "used" : "free", block->id[0], block->id[1], blkoff, - seg_len, is_root ? " (root)" : ""); - - blocks_bytes += seg_len; - if (seg_len < smallest_block) smallest_block = seg_len; - if (seg_len > largest_block) largest_block = seg_len; - - if (is_root && !used) - bad_root_block = 1; - - if (used) { - used_blocks++; - used_size += seg_len; - - /* Root block must be an nk-block. */ - if (is_root && (block->id[0] != 'n' || block->id[1] != 'k')) - bad_root_block = 1; - - /* Note this blkoff is a valid address. */ - BITMAP_SET (h->bitmap, blkoff); - } - } - } - - if (!seen_root_block) { - fprintf (stderr, "hivex: %s: no root block found\n", filename); - errno = ENOTSUP; - goto error; - } - - if (bad_root_block) { - fprintf (stderr, "hivex: %s: bad root block (free or not nk)\n", filename); - errno = ENOTSUP; - goto error; - } - - if (h->msglvl >= 1) - fprintf (stderr, - "hivex_open: successfully read Windows Registry hive file:\n" - " pages: %zu [sml: %zu, lge: %zu]\n" - " blocks: %zu [sml: %zu, avg: %zu, lge: %zu]\n" - " blocks used: %zu\n" - " bytes used: %zu\n", - pages, smallest_page, largest_page, - blocks, smallest_block, blocks_bytes / blocks, largest_block, - used_blocks, used_size); - - return h; - - error:; - int err = errno; - if (h) { - free (h->bitmap); - if (h->addr && h->size && h->addr != MAP_FAILED) { - if (!h->writable) - munmap (h->addr, h->size); - else - free (h->addr); - } - if (h->fd >= 0) - close (h->fd); - free (h->filename); - free (h); - } - errno = err; - return NULL; -} - -int -hivex_close (hive_h *h) -{ - int r; - - free (h->bitmap); - if (!h->writable) - munmap (h->addr, h->size); - else - free (h->addr); - r = close (h->fd); - free (h->filename); - free (h); - - return r; -} - -/*---------------------------------------------------------------------- - * Reading. - */ - -hive_node_h -hivex_root (hive_h *h) -{ - hive_node_h ret = h->rootoffs; - if (!IS_VALID_BLOCK (h, ret)) { - errno = ENOKEY; - return 0; - } - return ret; -} - -char * -hivex_node_name (hive_h *h, hive_node_h node) -{ - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return NULL; - } - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - /* AFAIK the node name is always plain ASCII, so no conversion - * to UTF-8 is necessary. However we do need to nul-terminate - * the string. - */ - - /* nk->name_len is unsigned, 16 bit, so this is safe ... However - * we have to make sure the length doesn't exceed the block length. - */ - size_t len = le16toh (nk->name_len); - size_t seg_len = block_len (h, node, NULL); - if (sizeof (struct ntreg_nk_record) + len - 1 > seg_len) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_name: returning EFAULT because node name is too long (%zu, %zu)\n", - len, seg_len); - errno = EFAULT; - return NULL; - } - - char *ret = malloc (len + 1); - if (ret == NULL) - return NULL; - memcpy (ret, nk->name, len); - ret[len] = '\0'; - return ret; -} - -#if 0 -/* I think the documentation for the sk and classname fields in the nk - * record is wrong, or else the offset field is in the wrong place. - * Otherwise this makes no sense. Disabled this for now -- it's not - * useful for reading the registry anyway. - */ - -hive_security_h -hivex_node_security (hive_h *h, hive_node_h node) -{ - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return 0; - } - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - hive_node_h ret = le32toh (nk->sk); - ret += 0x1000; - if (!IS_VALID_BLOCK (h, ret)) { - errno = EFAULT; - return 0; - } - return ret; -} - -hive_classname_h -hivex_node_classname (hive_h *h, hive_node_h node) -{ - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return 0; - } - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - hive_node_h ret = le32toh (nk->classname); - ret += 0x1000; - if (!IS_VALID_BLOCK (h, ret)) { - errno = EFAULT; - return 0; - } - return ret; -} -#endif - -/* Structure for returning 0-terminated lists of offsets (nodes, - * values, etc). - */ -struct offset_list { - size_t *offsets; - size_t len; - size_t alloc; -}; - -static void -init_offset_list (struct offset_list *list) -{ - list->len = 0; - list->alloc = 0; - list->offsets = NULL; -} - -#define INIT_OFFSET_LIST(name) \ - struct offset_list name; \ - init_offset_list (&name) - -/* Preallocates the offset_list, but doesn't make the contents longer. */ -static int -grow_offset_list (struct offset_list *list, size_t alloc) -{ - assert (alloc >= list->len); - size_t *p = realloc (list->offsets, alloc * sizeof (size_t)); - if (p == NULL) - return -1; - list->offsets = p; - list->alloc = alloc; - return 0; -} - -static int -add_to_offset_list (struct offset_list *list, size_t offset) -{ - if (list->len >= list->alloc) { - if (grow_offset_list (list, list->alloc ? list->alloc * 2 : 4) == -1) - return -1; - } - list->offsets[list->len] = offset; - list->len++; - return 0; -} - -static void -free_offset_list (struct offset_list *list) -{ - free (list->offsets); -} - -static size_t * -return_offset_list (struct offset_list *list) -{ - if (add_to_offset_list (list, 0) == -1) - return NULL; - return list->offsets; /* caller frees */ -} - -/* Iterate over children, returning child nodes and intermediate blocks. */ -#define GET_CHILDREN_NO_CHECK_NK 1 - -static int -get_children (hive_h *h, hive_node_h node, - hive_node_h **children_ret, size_t **blocks_ret, - int flags) -{ - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return -1; - } - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - size_t nr_subkeys_in_nk = le32toh (nk->nr_subkeys); - - INIT_OFFSET_LIST (children); - INIT_OFFSET_LIST (blocks); - - /* Deal with the common "no subkeys" case quickly. */ - if (nr_subkeys_in_nk == 0) - goto ok; - - /* Arbitrarily limit the number of subkeys we will ever deal with. */ - if (nr_subkeys_in_nk > HIVEX_MAX_SUBKEYS) { - errno = ERANGE; - goto error; - } - - /* Preallocate space for the children. */ - if (grow_offset_list (&children, nr_subkeys_in_nk) == -1) - goto error; - - /* The subkey_lf field can point either to an lf-record, which is - * the common case, or if there are lots of subkeys, to an - * ri-record. - */ - size_t subkey_lf = le32toh (nk->subkey_lf); - subkey_lf += 0x1000; - if (!IS_VALID_BLOCK (h, subkey_lf)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: returning EFAULT because subkey_lf is not a valid block (0x%zx)\n", - subkey_lf); - errno = EFAULT; - goto error; - } - - if (add_to_offset_list (&blocks, subkey_lf) == -1) - goto error; - - struct ntreg_hbin_block *block = - (struct ntreg_hbin_block *) (h->addr + subkey_lf); - - /* Points to lf-record? (Note, also "lh" but that is basically the - * same as "lf" as far as we are concerned here). - */ - if (block->id[0] == 'l' && (block->id[1] == 'f' || block->id[1] == 'h')) { - struct ntreg_lf_record *lf = (struct ntreg_lf_record *) block; - - /* Check number of subkeys in the nk-record matches number of subkeys - * in the lf-record. - */ - size_t nr_subkeys_in_lf = le16toh (lf->nr_keys); - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu, nr_subkeys_in_lf = %zu\n", - nr_subkeys_in_nk, nr_subkeys_in_lf); - - if (nr_subkeys_in_nk != nr_subkeys_in_lf) { - errno = ENOTSUP; - goto error; - } - - size_t len = block_len (h, subkey_lf, NULL); - if (8 + nr_subkeys_in_lf * 8 > len) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: returning EFAULT because too many subkeys (%zu, %zu)\n", - nr_subkeys_in_lf, len); - errno = EFAULT; - goto error; - } - - size_t i; - for (i = 0; i < nr_subkeys_in_lf; ++i) { - hive_node_h subkey = le32toh (lf->keys[i].offset); - subkey += 0x1000; - if (!(flags & GET_CHILDREN_NO_CHECK_NK)) { - if (!IS_VALID_BLOCK (h, subkey)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: returning EFAULT because subkey is not a valid block (0x%zx)\n", - subkey); - errno = EFAULT; - goto error; - } - } - if (add_to_offset_list (&children, subkey) == -1) - goto error; - } - goto ok; - } - /* Points to ri-record? */ - else if (block->id[0] == 'r' && block->id[1] == 'i') { - struct ntreg_ri_record *ri = (struct ntreg_ri_record *) block; - - size_t nr_offsets = le16toh (ri->nr_offsets); - - /* Count total number of children. */ - size_t i, count = 0; - for (i = 0; i < nr_offsets; ++i) { - hive_node_h offset = le32toh (ri->offset[i]); - offset += 0x1000; - if (!IS_VALID_BLOCK (h, offset)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n", - offset); - errno = EFAULT; - goto error; - } - if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) { - if (h->msglvl >= 2) - fprintf (stderr, "get_children: returning ENOTSUP because ri-record offset does not point to lf/lh (0x%zx)\n", - offset); - errno = ENOTSUP; - goto error; - } - - if (add_to_offset_list (&blocks, offset) == -1) - goto error; - - struct ntreg_lf_record *lf = - (struct ntreg_lf_record *) (h->addr + offset); - - count += le16toh (lf->nr_keys); - } - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: nr_subkeys_in_nk = %zu, counted = %zu\n", - nr_subkeys_in_nk, count); - - if (nr_subkeys_in_nk != count) { - errno = ENOTSUP; - goto error; - } - - /* Copy list of children. Note nr_subkeys_in_nk is limited to - * something reasonable above. - */ - for (i = 0; i < nr_offsets; ++i) { - hive_node_h offset = le32toh (ri->offset[i]); - offset += 0x1000; - if (!IS_VALID_BLOCK (h, offset)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: returning EFAULT because ri-offset is not a valid block (0x%zx)\n", - offset); - errno = EFAULT; - goto error; - } - if (!BLOCK_ID_EQ (h, offset, "lf") && !BLOCK_ID_EQ (h, offset, "lh")) { - if (h->msglvl >= 2) - fprintf (stderr, "get_children: returning ENOTSUP because ri-record offset does not point to lf/lh (0x%zx)\n", - offset); - errno = ENOTSUP; - goto error; - } - - struct ntreg_lf_record *lf = - (struct ntreg_lf_record *) (h->addr + offset); - - size_t j; - for (j = 0; j < le16toh (lf->nr_keys); ++j) { - hive_node_h subkey = le32toh (lf->keys[j].offset); - subkey += 0x1000; - if (!(flags & GET_CHILDREN_NO_CHECK_NK)) { - if (!IS_VALID_BLOCK (h, subkey)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_children: returning EFAULT because indirect subkey is not a valid block (0x%zx)\n", - subkey); - errno = EFAULT; - goto error; - } - } - if (add_to_offset_list (&children, subkey) == -1) - goto error; - } - } - goto ok; - } - /* else not supported, set errno and fall through */ - if (h->msglvl >= 2) - fprintf (stderr, "get_children: returning ENOTSUP because subkey block is not lf/lh/ri (0x%zx, %d, %d)\n", - subkey_lf, block->id[0], block->id[1]); - errno = ENOTSUP; - error: - free_offset_list (&children); - free_offset_list (&blocks); - return -1; - - ok: - *children_ret = return_offset_list (&children); - *blocks_ret = return_offset_list (&blocks); - if (!*children_ret || !*blocks_ret) - goto error; - return 0; -} - -hive_node_h * -hivex_node_children (hive_h *h, hive_node_h node) -{ - hive_node_h *children; - size_t *blocks; - - if (get_children (h, node, &children, &blocks, 0) == -1) - return NULL; - - free (blocks); - return children; -} - -/* Very inefficient, but at least having a separate API call - * allows us to make it more efficient in future. - */ -hive_node_h -hivex_node_get_child (hive_h *h, hive_node_h node, const char *nname) -{ - hive_node_h *children = NULL; - char *name = NULL; - hive_node_h ret = 0; - - children = hivex_node_children (h, node); - if (!children) goto error; - - size_t i; - for (i = 0; children[i] != 0; ++i) { - name = hivex_node_name (h, children[i]); - if (!name) goto error; - if (STRCASEEQ (name, nname)) { - ret = children[i]; - break; - } - free (name); name = NULL; - } - - error: - free (children); - free (name); - return ret; -} - -hive_node_h -hivex_node_parent (hive_h *h, hive_node_h node) -{ - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return 0; - } - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - hive_node_h ret = le32toh (nk->parent); - ret += 0x1000; - if (!IS_VALID_BLOCK (h, ret)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_parent: returning EFAULT because parent is not a valid block (0x%zx)\n", - ret); - errno = EFAULT; - return 0; - } - return ret; -} - -static int -get_values (hive_h *h, hive_node_h node, - hive_value_h **values_ret, size_t **blocks_ret) -{ - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return -1; - } - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - size_t nr_values = le32toh (nk->nr_values); - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_values: nr_values = %zu\n", nr_values); - - INIT_OFFSET_LIST (values); - INIT_OFFSET_LIST (blocks); - - /* Deal with the common "no values" case quickly. */ - if (nr_values == 0) - goto ok; - - /* Arbitrarily limit the number of values we will ever deal with. */ - if (nr_values > HIVEX_MAX_VALUES) { - errno = ERANGE; - goto error; - } - - /* Preallocate space for the values. */ - if (grow_offset_list (&values, nr_values) == -1) - goto error; - - /* Get the value list and check it looks reasonable. */ - size_t vlist_offset = le32toh (nk->vallist); - vlist_offset += 0x1000; - if (!IS_VALID_BLOCK (h, vlist_offset)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_values: returning EFAULT because value list is not a valid block (0x%zx)\n", - vlist_offset); - errno = EFAULT; - goto error; - } - - if (add_to_offset_list (&blocks, vlist_offset) == -1) - goto error; - - struct ntreg_value_list *vlist = - (struct ntreg_value_list *) (h->addr + vlist_offset); - - size_t len = block_len (h, vlist_offset, NULL); - if (4 + nr_values * 4 > len) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_values: returning EFAULT because value list is too long (%zu, %zu)\n", - nr_values, len); - errno = EFAULT; - goto error; - } - - size_t i; - for (i = 0; i < nr_values; ++i) { - hive_node_h value = vlist->offset[i]; - value += 0x1000; - if (!IS_VALID_BLOCK (h, value)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_values: returning EFAULT because value is not a valid block (0x%zx)\n", - value); - errno = EFAULT; - goto error; - } - if (add_to_offset_list (&values, value) == -1) - goto error; - } - - ok: - *values_ret = return_offset_list (&values); - *blocks_ret = return_offset_list (&blocks); - if (!*values_ret || !*blocks_ret) - goto error; - return 0; - - error: - free_offset_list (&values); - free_offset_list (&blocks); - return -1; -} - -hive_value_h * -hivex_node_values (hive_h *h, hive_node_h node) -{ - hive_value_h *values; - size_t *blocks; - - if (get_values (h, node, &values, &blocks) == -1) - return NULL; - - free (blocks); - return values; -} - -/* Very inefficient, but at least having a separate API call - * allows us to make it more efficient in future. - */ -hive_value_h -hivex_node_get_value (hive_h *h, hive_node_h node, const char *key) -{ - hive_value_h *values = NULL; - char *name = NULL; - hive_value_h ret = 0; - - values = hivex_node_values (h, node); - if (!values) goto error; - - size_t i; - for (i = 0; values[i] != 0; ++i) { - name = hivex_value_key (h, values[i]); - if (!name) goto error; - if (STRCASEEQ (name, key)) { - ret = values[i]; - break; - } - free (name); name = NULL; - } - - error: - free (values); - free (name); - return ret; -} - -char * -hivex_value_key (hive_h *h, hive_value_h value) -{ - if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) { - errno = EINVAL; - return 0; - } - - struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value); - - /* AFAIK the key is always plain ASCII, so no conversion to UTF-8 is - * necessary. However we do need to nul-terminate the string. - */ - - /* vk->name_len is unsigned, 16 bit, so this is safe ... However - * we have to make sure the length doesn't exceed the block length. - */ - size_t len = le16toh (vk->name_len); - size_t seg_len = block_len (h, value, NULL); - if (sizeof (struct ntreg_vk_record) + len - 1 > seg_len) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_value_key: returning EFAULT because key length is too long (%zu, %zu)\n", - len, seg_len); - errno = EFAULT; - return NULL; - } - - char *ret = malloc (len + 1); - if (ret == NULL) - return NULL; - memcpy (ret, vk->name, len); - ret[len] = '\0'; - return ret; -} - -int -hivex_value_type (hive_h *h, hive_value_h value, hive_type *t, size_t *len) -{ - if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) { - errno = EINVAL; - return -1; - } - - struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value); - - if (t) - *t = le32toh (vk->data_type); - - if (len) { - *len = le32toh (vk->data_len); - *len &= 0x7fffffff; /* top bit indicates if data is stored inline */ - } - - return 0; -} - -char * -hivex_value_value (hive_h *h, hive_value_h value, - hive_type *t_rtn, size_t *len_rtn) -{ - if (!IS_VALID_BLOCK (h, value) || !BLOCK_ID_EQ (h, value, "vk")) { - errno = EINVAL; - return NULL; - } - - struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + value); - - hive_type t; - size_t len; - int is_inline; - - t = le32toh (vk->data_type); - - len = le32toh (vk->data_len); - is_inline = !!(len & 0x80000000); - len &= 0x7fffffff; - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_value_value: value=0x%zx, t=%d, len=%zu, inline=%d\n", - value, t, len, is_inline); - - if (t_rtn) - *t_rtn = t; - if (len_rtn) - *len_rtn = len; - - if (is_inline && len > 4) { - errno = ENOTSUP; - return NULL; - } - - /* Arbitrarily limit the length that we will read. */ - if (len > HIVEX_MAX_VALUE_LEN) { - errno = ERANGE; - return NULL; - } - - char *ret = malloc (len); - if (ret == NULL) - return NULL; - - if (is_inline) { - memcpy (ret, (char *) &vk->data_offset, len); - return ret; - } - - size_t data_offset = le32toh (vk->data_offset); - data_offset += 0x1000; - if (!IS_VALID_BLOCK (h, data_offset)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_value_value: returning EFAULT because data offset is not a valid block (0x%zx)\n", - data_offset); - errno = EFAULT; - free (ret); - return NULL; - } - - /* Check that the declared size isn't larger than the block its in. - * - * XXX Some apparently valid registries are seen to have this, - * so turn this into a warning and substitute the smaller length - * instead. - */ - size_t blen = block_len (h, data_offset, NULL); - if (len > blen - 4 /* subtract 4 for block header */) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_value_value: warning: declared data length is longer than the block it is in (data 0x%zx, data len %zu, block len %zu)\n", - data_offset, len, blen); - len = blen - 4; - } - - char *data = h->addr + data_offset + 4; - memcpy (ret, data, len); - return ret; -} - -static char * -windows_utf16_to_utf8 (/* const */ char *input, size_t len) -{ - iconv_t ic = iconv_open ("UTF-8", "UTF-16"); - if (ic == (iconv_t) -1) - return NULL; - - /* iconv(3) has an insane interface ... */ - - /* Mostly UTF-8 will be smaller, so this is a good initial guess. */ - size_t outalloc = len; - - again:; - size_t inlen = len; - size_t outlen = outalloc; - char *out = malloc (outlen + 1); - if (out == NULL) { - int err = errno; - iconv_close (ic); - errno = err; - return NULL; - } - char *inp = input; - char *outp = out; - - size_t r = iconv (ic, &inp, &inlen, &outp, &outlen); - if (r == (size_t) -1) { - if (errno == E2BIG) { - size_t prev = outalloc; - /* Try again with a larger output buffer. */ - free (out); - outalloc *= 2; - if (outalloc < prev) - return NULL; - goto again; - } - else { - /* Else some conversion failure, eg. EILSEQ, EINVAL. */ - int err = errno; - iconv_close (ic); - free (out); - errno = err; - return NULL; - } - } - - *outp = '\0'; - iconv_close (ic); - - return out; -} - -char * -hivex_value_string (hive_h *h, hive_value_h value) -{ - hive_type t; - size_t len; - char *data = hivex_value_value (h, value, &t, &len); - - if (data == NULL) - return NULL; - - if (t != hive_t_string && t != hive_t_expand_string && t != hive_t_link) { - free (data); - errno = EINVAL; - return NULL; - } - - char *ret = windows_utf16_to_utf8 (data, len); - free (data); - if (ret == NULL) - return NULL; - - return ret; -} - -static void -free_strings (char **argv) -{ - if (argv) { - size_t i; - - for (i = 0; argv[i] != NULL; ++i) - free (argv[i]); - free (argv); - } -} - -/* Get the length of a UTF-16 format string. Handle the string as - * pairs of bytes, looking for the first \0\0 pair. - */ -static size_t -utf16_string_len_in_bytes (const char *str) -{ - size_t ret = 0; - - while (str[0] || str[1]) { - str += 2; - ret += 2; - } - - return ret; -} - -/* http://blogs.msdn.com/oldnewthing/archive/2009/10/08/9904646.aspx */ -char ** -hivex_value_multiple_strings (hive_h *h, hive_value_h value) -{ - hive_type t; - size_t len; - char *data = hivex_value_value (h, value, &t, &len); - - if (data == NULL) - return NULL; - - if (t != hive_t_multiple_strings) { - free (data); - errno = EINVAL; - return NULL; - } - - size_t nr_strings = 0; - char **ret = malloc ((1 + nr_strings) * sizeof (char *)); - if (ret == NULL) { - free (data); - return NULL; - } - ret[0] = NULL; - - char *p = data; - size_t plen; - - while (p < data + len && (plen = utf16_string_len_in_bytes (p)) > 0) { - nr_strings++; - char **ret2 = realloc (ret, (1 + nr_strings) * sizeof (char *)); - if (ret2 == NULL) { - free_strings (ret); - free (data); - return NULL; - } - ret = ret2; - - ret[nr_strings-1] = windows_utf16_to_utf8 (p, plen); - ret[nr_strings] = NULL; - if (ret[nr_strings-1] == NULL) { - free_strings (ret); - free (data); - return NULL; - } - - p += plen + 2 /* skip over UTF-16 \0\0 at the end of this string */; - } - - free (data); - return ret; -} - -int32_t -hivex_value_dword (hive_h *h, hive_value_h value) -{ - hive_type t; - size_t len; - char *data = hivex_value_value (h, value, &t, &len); - - if (data == NULL) - return -1; - - if ((t != hive_t_dword && t != hive_t_dword_be) || len != 4) { - free (data); - errno = EINVAL; - return -1; - } - - int32_t ret = *(int32_t*)data; - free (data); - if (t == hive_t_dword) /* little endian */ - ret = le32toh (ret); - else - ret = be32toh (ret); - - return ret; -} - -int64_t -hivex_value_qword (hive_h *h, hive_value_h value) -{ - hive_type t; - size_t len; - char *data = hivex_value_value (h, value, &t, &len); - - if (data == NULL) - return -1; - - if (t != hive_t_qword || len != 8) { - free (data); - errno = EINVAL; - return -1; - } - - int64_t ret = *(int64_t*)data; - free (data); - ret = le64toh (ret); /* always little endian */ - - return ret; -} - -/*---------------------------------------------------------------------- - * Visiting. - */ - -int -hivex_visit (hive_h *h, const struct hivex_visitor *visitor, size_t len, - void *opaque, int flags) -{ - return hivex_visit_node (h, hivex_root (h), visitor, len, opaque, flags); -} - -static int hivex__visit_node (hive_h *h, hive_node_h node, const struct hivex_visitor *vtor, char *unvisited, void *opaque, int flags); - -int -hivex_visit_node (hive_h *h, hive_node_h node, - const struct hivex_visitor *visitor, size_t len, void *opaque, - int flags) -{ - struct hivex_visitor vtor; - memset (&vtor, 0, sizeof vtor); - - /* Note that len might be larger *or smaller* than the expected size. */ - size_t copysize = len <= sizeof vtor ? len : sizeof vtor; - memcpy (&vtor, visitor, copysize); - - /* This bitmap records unvisited nodes, so we don't loop if the - * registry contains cycles. - */ - char *unvisited = malloc (1 + h->size / 32); - if (unvisited == NULL) - return -1; - memcpy (unvisited, h->bitmap, 1 + h->size / 32); - - int r = hivex__visit_node (h, node, &vtor, unvisited, opaque, flags); - free (unvisited); - return r; -} - -static int -hivex__visit_node (hive_h *h, hive_node_h node, - const struct hivex_visitor *vtor, char *unvisited, - void *opaque, int flags) -{ - int skip_bad = flags & HIVEX_VISIT_SKIP_BAD; - char *name = NULL; - hive_value_h *values = NULL; - hive_node_h *children = NULL; - char *key = NULL; - char *str = NULL; - char **strs = NULL; - int i; - - /* Return -1 on all callback errors. However on internal errors, - * check if skip_bad is set and suppress those errors if so. - */ - int ret = -1; - - if (!BITMAP_TST (unvisited, node)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex__visit_node: contains cycle: visited node 0x%zx already\n", - node); - - errno = ELOOP; - return skip_bad ? 0 : -1; - } - BITMAP_CLR (unvisited, node); - - name = hivex_node_name (h, node); - if (!name) return skip_bad ? 0 : -1; - if (vtor->node_start && vtor->node_start (h, opaque, node, name) == -1) - goto error; - - values = hivex_node_values (h, node); - if (!values) { - ret = skip_bad ? 0 : -1; - goto error; - } - - for (i = 0; values[i] != 0; ++i) { - hive_type t; - size_t len; - - if (hivex_value_type (h, values[i], &t, &len) == -1) { - ret = skip_bad ? 0 : -1; - goto error; - } - - key = hivex_value_key (h, values[i]); - if (key == NULL) { - ret = skip_bad ? 0 : -1; - goto error; - } - - if (vtor->value_any) { - str = hivex_value_value (h, values[i], &t, &len); - if (str == NULL) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (vtor->value_any (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - } - else { - switch (t) { - case hive_t_none: - str = hivex_value_value (h, values[i], &t, &len); - if (str == NULL) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (t != hive_t_none) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (vtor->value_none && - vtor->value_none (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - break; - - case hive_t_string: - case hive_t_expand_string: - case hive_t_link: - str = hivex_value_string (h, values[i]); - if (str == NULL) { - if (errno != EILSEQ && errno != EINVAL) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (vtor->value_string_invalid_utf16) { - str = hivex_value_value (h, values[i], &t, &len); - if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - } - break; - } - if (vtor->value_string && - vtor->value_string (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - break; - - case hive_t_dword: - case hive_t_dword_be: { - int32_t i32 = hivex_value_dword (h, values[i]); - if (vtor->value_dword && - vtor->value_dword (h, opaque, node, values[i], t, len, key, i32) == -1) - goto error; - break; - } - - case hive_t_qword: { - int64_t i64 = hivex_value_qword (h, values[i]); - if (vtor->value_qword && - vtor->value_qword (h, opaque, node, values[i], t, len, key, i64) == -1) - goto error; - break; - } - - case hive_t_binary: - str = hivex_value_value (h, values[i], &t, &len); - if (str == NULL) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (t != hive_t_binary) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (vtor->value_binary && - vtor->value_binary (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - break; - - case hive_t_multiple_strings: - strs = hivex_value_multiple_strings (h, values[i]); - if (strs == NULL) { - if (errno != EILSEQ && errno != EINVAL) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (vtor->value_string_invalid_utf16) { - str = hivex_value_value (h, values[i], &t, &len); - if (vtor->value_string_invalid_utf16 (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - } - break; - } - if (vtor->value_multiple_strings && - vtor->value_multiple_strings (h, opaque, node, values[i], t, len, key, strs) == -1) - goto error; - free_strings (strs); strs = NULL; - break; - - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - default: - str = hivex_value_value (h, values[i], &t, &len); - if (str == NULL) { - ret = skip_bad ? 0 : -1; - goto error; - } - if (vtor->value_other && - vtor->value_other (h, opaque, node, values[i], t, len, key, str) == -1) - goto error; - free (str); str = NULL; - break; - } - } - - free (key); key = NULL; - } - - children = hivex_node_children (h, node); - if (children == NULL) { - ret = skip_bad ? 0 : -1; - goto error; - } - - for (i = 0; children[i] != 0; ++i) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex__visit_node: %s: visiting subkey %d (0x%zx)\n", - name, i, children[i]); - - if (hivex__visit_node (h, children[i], vtor, unvisited, opaque, flags) == -1) - goto error; - } - - if (vtor->node_end && vtor->node_end (h, opaque, node, name) == -1) - goto error; - - ret = 0; - - error: - free (name); - free (values); - free (children); - free (key); - free (str); - free_strings (strs); - return ret; -} - -/*---------------------------------------------------------------------- - * Writing. - */ - -/* Allocate an hbin (page), extending the malloc'd space if necessary, - * and updating the hive handle fields (but NOT the hive disk header - * -- the hive disk header is updated when we commit). This function - * also extends the bitmap if necessary. - * - * 'allocation_hint' is the size of the block allocation we would like - * to make. Normally registry blocks are very small (avg 50 bytes) - * and are contained in standard-sized pages (4KB), but the registry - * can support blocks which are larger than a standard page, in which - * case it creates a page of 8KB, 12KB etc. - * - * Returns: - * > 0 : offset of first usable byte of new page (after page header) - * 0 : error (errno set) - */ -static size_t -allocate_page (hive_h *h, size_t allocation_hint) -{ - /* In almost all cases this will be 1. */ - size_t nr_4k_pages = - 1 + (allocation_hint + sizeof (struct ntreg_hbin_page) - 1) / 4096; - assert (nr_4k_pages >= 1); - - /* 'extend' is the number of bytes to extend the file by. Note that - * hives found in the wild often contain slack between 'endpages' - * and the actual end of the file, so we don't always need to make - * the file larger. - */ - ssize_t extend = h->endpages + nr_4k_pages * 4096 - h->size; - - if (h->msglvl >= 2) { - fprintf (stderr, "allocate_page: current endpages = 0x%zx, current size = 0x%zx\n", - h->endpages, h->size); - fprintf (stderr, "allocate_page: extending file by %zd bytes (<= 0 if no extension)\n", - extend); - } - - if (extend > 0) { - size_t oldsize = h->size; - size_t newsize = h->size + extend; - char *newaddr = realloc (h->addr, newsize); - if (newaddr == NULL) - return 0; - - size_t oldbitmapsize = 1 + oldsize / 32; - size_t newbitmapsize = 1 + newsize / 32; - char *newbitmap = realloc (h->bitmap, newbitmapsize); - if (newbitmap == NULL) { - free (newaddr); - return 0; - } - - h->addr = newaddr; - h->size = newsize; - h->bitmap = newbitmap; - - memset (h->addr + oldsize, 0, newsize - oldsize); - memset (h->bitmap + oldbitmapsize, 0, newbitmapsize - oldbitmapsize); - } - - size_t offset = h->endpages; - h->endpages += nr_4k_pages * 4096; - - if (h->msglvl >= 2) - fprintf (stderr, "allocate_page: new endpages = 0x%zx, new size = 0x%zx\n", - h->endpages, h->size); - - /* Write the hbin header. */ - struct ntreg_hbin_page *page = - (struct ntreg_hbin_page *) (h->addr + offset); - page->magic[0] = 'h'; - page->magic[1] = 'b'; - page->magic[2] = 'i'; - page->magic[3] = 'n'; - page->offset_first = htole32 (offset - 0x1000); - page->page_size = htole32 (nr_4k_pages * 4096); - memset (page->unknown, 0, sizeof (page->unknown)); - - if (h->msglvl >= 2) - fprintf (stderr, "allocate_page: new page at 0x%zx\n", offset); - - /* Offset of first usable byte after the header. */ - return offset + sizeof (struct ntreg_hbin_page); -} - -/* Allocate a single block, first allocating an hbin (page) at the end - * of the current file if necessary. NB. To keep the implementation - * simple and more likely to be correct, we do not reuse existing free - * blocks. - * - * seg_len is the size of the block (this INCLUDES the block header). - * The header of the block is initialized to -seg_len (negative to - * indicate used). id[2] is the block ID (type), eg. "nk" for nk- - * record. The block bitmap is updated to show this block as valid. - * The rest of the contents of the block will be zero. - * - * Returns: - * > 0 : offset of new block - * 0 : error (errno set) - */ -static size_t -allocate_block (hive_h *h, size_t seg_len, const char id[2]) -{ - if (!h->writable) { - errno = EROFS; - return 0; - } - - if (seg_len < 4) { - /* The caller probably forgot to include the header. Note that - * value lists have no ID field, so seg_len == 4 would be possible - * for them, albeit unusual. - */ - if (h->msglvl >= 2) - fprintf (stderr, "allocate_block: refusing too small allocation (%zu), returning ERANGE\n", - seg_len); - errno = ERANGE; - return 0; - } - - /* Refuse really large allocations. */ - if (seg_len > HIVEX_MAX_ALLOCATION) { - if (h->msglvl >= 2) - fprintf (stderr, "allocate_block: refusing large allocation (%zu), returning ERANGE\n", - seg_len); - errno = ERANGE; - return 0; - } - - /* Round up allocation to multiple of 8 bytes. All blocks must be - * on an 8 byte boundary. - */ - seg_len = (seg_len + 7) & ~7; - - /* Allocate a new page if necessary. */ - if (h->endblocks == 0 || h->endblocks + seg_len > h->endpages) { - size_t newendblocks = allocate_page (h, seg_len); - if (newendblocks == 0) - return 0; - h->endblocks = newendblocks; - } - - size_t offset = h->endblocks; - - if (h->msglvl >= 2) - fprintf (stderr, "allocate_block: new block at 0x%zx, size %zu\n", - offset, seg_len); - - struct ntreg_hbin_block *blockhdr = - (struct ntreg_hbin_block *) (h->addr + offset); - - blockhdr->seg_len = htole32 (- (int32_t) seg_len); - if (id[0] && id[1] && seg_len >= sizeof (struct ntreg_hbin_block)) { - blockhdr->id[0] = id[0]; - blockhdr->id[1] = id[1]; - } - - BITMAP_SET (h->bitmap, offset); - - h->endblocks += seg_len; - - /* If there is space after the last block in the last page, then we - * have to put a dummy free block header here to mark the rest of - * the page as free. - */ - ssize_t rem = h->endpages - h->endblocks; - if (rem > 0) { - if (h->msglvl >= 2) - fprintf (stderr, "allocate_block: marking remainder of page free starting at 0x%zx, size %zd\n", - h->endblocks, rem); - - assert (rem >= 4); - - blockhdr = (struct ntreg_hbin_block *) (h->addr + h->endblocks); - blockhdr->seg_len = htole32 ((int32_t) rem); - } - - return offset; -} - -/* 'offset' must point to a valid, used block. This function marks - * the block unused (by updating the seg_len field) and invalidates - * the bitmap. It does NOT do this recursively, so to avoid creating - * unreachable used blocks, callers may have to recurse over the hive - * structures. Also callers must ensure there are no references to - * this block from other parts of the hive. - */ -static void -mark_block_unused (hive_h *h, size_t offset) -{ - assert (h->writable); - assert (IS_VALID_BLOCK (h, offset)); - - if (h->msglvl >= 2) - fprintf (stderr, "mark_block_unused: marking 0x%zx unused\n", offset); - - struct ntreg_hbin_block *blockhdr = - (struct ntreg_hbin_block *) (h->addr + offset); - - size_t seg_len = block_len (h, offset, NULL); - blockhdr->seg_len = htole32 (seg_len); - - BITMAP_CLR (h->bitmap, offset); -} - -/* Delete all existing values at this node. */ -static int -delete_values (hive_h *h, hive_node_h node) -{ - assert (h->writable); - - hive_value_h *values; - size_t *blocks; - if (get_values (h, node, &values, &blocks) == -1) - return -1; - - size_t i; - for (i = 0; blocks[i] != 0; ++i) - mark_block_unused (h, blocks[i]); - - free (blocks); - - for (i = 0; values[i] != 0; ++i) { - struct ntreg_vk_record *vk = - (struct ntreg_vk_record *) (h->addr + values[i]); - - size_t len; - int is_inline; - len = le32toh (vk->data_len); - is_inline = !!(len & 0x80000000); /* top bit indicates is inline */ - len &= 0x7fffffff; - - if (!is_inline) { /* non-inline, so remove data block */ - size_t data_offset = le32toh (vk->data_offset); - data_offset += 0x1000; - mark_block_unused (h, data_offset); - } - - /* remove vk record */ - mark_block_unused (h, values[i]); - } - - free (values); - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - nk->nr_values = htole32 (0); - nk->vallist = htole32 (0xffffffff); - - return 0; -} - -int -hivex_commit (hive_h *h, const char *filename, int flags) -{ - if (flags != 0) { - errno = EINVAL; - return -1; - } - - if (!h->writable) { - errno = EROFS; - return -1; - } - - filename = filename ? : h->filename; - int fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666); - if (fd == -1) - return -1; - - /* Update the header fields. */ - uint32_t sequence = le32toh (h->hdr->sequence1); - sequence++; - h->hdr->sequence1 = htole32 (sequence); - h->hdr->sequence2 = htole32 (sequence); - /* XXX Ought to update h->hdr->last_modified. */ - h->hdr->blocks = htole32 (h->endpages - 0x1000); - - /* Recompute header checksum. */ - uint32_t sum = header_checksum (h); - h->hdr->csum = htole32 (sum); - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_commit: new header checksum: 0x%x\n", sum); - - if (full_write (fd, h->addr, h->size) != h->size) { - int err = errno; - close (fd); - errno = err; - return -1; - } - - if (close (fd) == -1) - return -1; - - return 0; -} - -/* Calculate the hash for a lf or lh record offset. - */ -static void -calc_hash (const char *type, const char *name, char *ret) -{ - size_t len = strlen (name); - - if (STRPREFIX (type, "lf")) - /* Old-style, not used in current registries. */ - memcpy (ret, name, len < 4 ? len : 4); - else { - /* New-style for lh-records. */ - size_t i, c; - uint32_t h = 0; - for (i = 0; i < len; ++i) { - c = c_toupper (name[i]); - h *= 37; - h += c; - } - *((uint32_t *) ret) = htole32 (h); - } -} - -/* Create a completely new lh-record containing just the single node. */ -static size_t -new_lh_record (hive_h *h, const char *name, hive_node_h node) -{ - static const char id[2] = { 'l', 'h' }; - size_t seg_len = sizeof (struct ntreg_lf_record); - size_t offset = allocate_block (h, seg_len, id); - if (offset == 0) - return 0; - - struct ntreg_lf_record *lh = (struct ntreg_lf_record *) (h->addr + offset); - lh->nr_keys = htole16 (1); - lh->keys[0].offset = htole32 (node - 0x1000); - calc_hash ("lh", name, lh->keys[0].hash); - - return offset; -} - -/* Insert node into existing lf/lh-record at position. - * This allocates a new record and marks the old one as unused. - */ -static size_t -insert_lf_record (hive_h *h, size_t old_offs, size_t posn, - const char *name, hive_node_h node) -{ - assert (IS_VALID_BLOCK (h, old_offs)); - - /* Work around C stupidity. - * http://www.redhat.com/archives/libguestfs/2010-February/msg00056.html - */ - int test = BLOCK_ID_EQ (h, old_offs, "lf") || BLOCK_ID_EQ (h, old_offs, "lh"); - assert (test); - - struct ntreg_lf_record *old_lf = - (struct ntreg_lf_record *) (h->addr + old_offs); - size_t nr_keys = le16toh (old_lf->nr_keys); - - nr_keys++; /* in new record ... */ - - size_t seg_len = sizeof (struct ntreg_lf_record) + (nr_keys-1) * 8; - size_t new_offs = allocate_block (h, seg_len, old_lf->id); - if (new_offs == 0) - return 0; - - struct ntreg_lf_record *new_lf = - (struct ntreg_lf_record *) (h->addr + new_offs); - new_lf->nr_keys = htole16 (nr_keys); - - /* Copy the keys until we reach posn, insert the new key there, then - * copy the remaining keys. - */ - size_t i; - for (i = 0; i < posn; ++i) - new_lf->keys[i] = old_lf->keys[i]; - - new_lf->keys[i].offset = htole32 (node - 0x1000); - calc_hash (new_lf->id, name, new_lf->keys[i].hash); - - for (i = posn+1; i < nr_keys; ++i) - new_lf->keys[i] = old_lf->keys[i-1]; - - /* Old block is unused, return new block. */ - mark_block_unused (h, old_offs); - return new_offs; -} - -/* Compare name with name in nk-record. */ -static int -compare_name_with_nk_name (hive_h *h, const char *name, hive_node_h nk_offs) -{ - assert (IS_VALID_BLOCK (h, nk_offs)); - assert (BLOCK_ID_EQ (h, nk_offs, "nk")); - - /* Name in nk is not necessarily nul-terminated. */ - char *nname = hivex_node_name (h, nk_offs); - - /* Unfortunately we don't have a way to return errors here. */ - if (!nname) { - perror ("compare_name_with_nk_name"); - return 0; - } - - int r = strcasecmp (name, nname); - free (nname); - - return r; -} - -hive_node_h -hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name) -{ - if (!h->writable) { - errno = EROFS; - return 0; - } - - if (!IS_VALID_BLOCK (h, parent) || !BLOCK_ID_EQ (h, parent, "nk")) { - errno = EINVAL; - return 0; - } - - if (name == NULL || strlen (name) == 0) { - errno = EINVAL; - return 0; - } - - if (hivex_node_get_child (h, parent, name) != 0) { - errno = EEXIST; - return 0; - } - - /* Create the new nk-record. */ - static const char nk_id[2] = { 'n', 'k' }; - size_t seg_len = sizeof (struct ntreg_nk_record) + strlen (name); - hive_node_h node = allocate_block (h, seg_len, nk_id); - if (node == 0) - return 0; - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_add_child: allocated new nk-record for child at 0x%zx\n", node); - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - nk->flags = htole16 (0x0020); /* key is ASCII. */ - nk->parent = htole32 (parent - 0x1000); - nk->subkey_lf = htole32 (0xffffffff); - nk->subkey_lf_volatile = htole32 (0xffffffff); - nk->vallist = htole32 (0xffffffff); - nk->classname = htole32 (0xffffffff); - nk->name_len = htole16 (strlen (name)); - strcpy (nk->name, name); - - /* Inherit parent sk. */ - struct ntreg_nk_record *parent_nk = - (struct ntreg_nk_record *) (h->addr + parent); - size_t parent_sk_offset = le32toh (parent_nk->sk); - parent_sk_offset += 0x1000; - if (!IS_VALID_BLOCK (h, parent_sk_offset) || - !BLOCK_ID_EQ (h, parent_sk_offset, "sk")) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_add_child: returning EFAULT because parent sk is not a valid block (%zu)\n", - parent_sk_offset); - errno = EFAULT; - return 0; - } - struct ntreg_sk_record *sk = - (struct ntreg_sk_record *) (h->addr + parent_sk_offset); - sk->refcount = htole32 (le32toh (sk->refcount) + 1); - nk->sk = htole32 (parent_sk_offset - 0x1000); - - /* Inherit parent timestamp. */ - memcpy (nk->timestamp, parent_nk->timestamp, sizeof (parent_nk->timestamp)); - - /* What I found out the hard way (not documented anywhere): the - * subkeys in lh-records must be kept sorted. If you just add a - * subkey in a non-sorted position (eg. just add it at the end) then - * Windows won't see the subkey _and_ Windows will corrupt the hive - * itself when it modifies or saves it. - * - * So use get_children() to get a list of intermediate - * lf/lh-records. get_children() returns these in reading order - * (which is sorted), so we look for the lf/lh-records in sequence - * until we find the key name just after the one we are inserting, - * and we insert the subkey just before it. - * - * The only other case is the no-subkeys case, where we have to - * create a brand new lh-record. - */ - hive_node_h *unused; - size_t *blocks; - - if (get_children (h, parent, &unused, &blocks, 0) == -1) - return 0; - free (unused); - - size_t i, j; - size_t nr_subkeys_in_parent_nk = le32toh (parent_nk->nr_subkeys); - if (nr_subkeys_in_parent_nk == 0) { /* No subkeys case. */ - /* Free up any existing intermediate blocks. */ - for (i = 0; blocks[i] != 0; ++i) - mark_block_unused (h, blocks[i]); - size_t lh_offs = new_lh_record (h, name, node); - if (lh_offs == 0) { - free (blocks); - return 0; - } - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_add_child: no keys, allocated new lh-record at 0x%zx\n", lh_offs); - - parent_nk->subkey_lf = htole32 (lh_offs - 0x1000); - } - else { /* Insert subkeys case. */ - size_t old_offs = 0, new_offs = 0; - struct ntreg_lf_record *old_lf = NULL; - - /* Find lf/lh key name just after the one we are inserting. */ - for (i = 0; blocks[i] != 0; ++i) { - if (BLOCK_ID_EQ (h, blocks[i], "lf") || - BLOCK_ID_EQ (h, blocks[i], "lh")) { - old_offs = blocks[i]; - old_lf = (struct ntreg_lf_record *) (h->addr + old_offs); - for (j = 0; j < le16toh (old_lf->nr_keys); ++j) { - hive_node_h nk_offs = le32toh (old_lf->keys[j].offset); - nk_offs += 0x1000; - if (compare_name_with_nk_name (h, name, nk_offs) < 0) - goto insert_it; - } - } - } - - /* Insert it at the end. - * old_offs points to the last lf record, set j. - */ - assert (old_offs != 0); /* should never happen if nr_subkeys > 0 */ - j = le16toh (old_lf->nr_keys); - - /* Insert it. */ - insert_it: - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_add_child: insert key in existing lh-record at 0x%zx, posn %zu\n", old_offs, j); - - new_offs = insert_lf_record (h, old_offs, j, name, node); - if (new_offs == 0) { - free (blocks); - return 0; - } - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_add_child: new lh-record at 0x%zx\n", - new_offs); - - /* If the lf/lh-record was directly referenced by the parent nk, - * then update the parent nk. - */ - if (le32toh (parent_nk->subkey_lf) + 0x1000 == old_offs) - parent_nk->subkey_lf = htole32 (new_offs - 0x1000); - /* Else we have to look for the intermediate ri-record and update - * that in-place. - */ - else { - for (i = 0; blocks[i] != 0; ++i) { - if (BLOCK_ID_EQ (h, blocks[i], "ri")) { - struct ntreg_ri_record *ri = - (struct ntreg_ri_record *) (h->addr + blocks[i]); - for (j = 0; j < le16toh (ri->nr_offsets); ++j) - if (le32toh (ri->offset[j] + 0x1000) == old_offs) { - ri->offset[j] = htole32 (new_offs - 0x1000); - goto found_it; - } - } - } - - /* Not found .. This is an internal error. */ - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_add_child: returning ENOTSUP because could not find ri->lf link\n"); - errno = ENOTSUP; - free (blocks); - return 0; - - found_it: - ; - } - } - - free (blocks); - - /* Update nr_subkeys in parent nk. */ - nr_subkeys_in_parent_nk++; - parent_nk->nr_subkeys = htole32 (nr_subkeys_in_parent_nk); - - /* Update max_subkey_name_len in parent nk. */ - uint16_t max = le16toh (parent_nk->max_subkey_name_len); - if (max < strlen (name) * 2) /* *2 because "recoded" in UTF16-LE. */ - parent_nk->max_subkey_name_len = htole16 (strlen (name) * 2); - - return node; -} - -/* Decrement the refcount of an sk-record, and if it reaches zero, - * unlink it from the chain and delete it. - */ -static int -delete_sk (hive_h *h, size_t sk_offset) -{ - if (!IS_VALID_BLOCK (h, sk_offset) || !BLOCK_ID_EQ (h, sk_offset, "sk")) { - if (h->msglvl >= 2) - fprintf (stderr, "delete_sk: not an sk record: 0x%zx\n", sk_offset); - errno = EFAULT; - return -1; - } - - struct ntreg_sk_record *sk = (struct ntreg_sk_record *) (h->addr + sk_offset); - - if (sk->refcount == 0) { - if (h->msglvl >= 2) - fprintf (stderr, "delete_sk: sk record already has refcount 0: 0x%zx\n", - sk_offset); - errno = EINVAL; - return -1; - } - - sk->refcount--; - - if (sk->refcount == 0) { - size_t sk_prev_offset = sk->sk_prev; - sk_prev_offset += 0x1000; - - size_t sk_next_offset = sk->sk_next; - sk_next_offset += 0x1000; - - /* Update sk_prev/sk_next SKs, unless they both point back to this - * cell in which case we are deleting the last SK. - */ - if (sk_prev_offset != sk_offset && sk_next_offset != sk_offset) { - struct ntreg_sk_record *sk_prev = - (struct ntreg_sk_record *) (h->addr + sk_prev_offset); - struct ntreg_sk_record *sk_next = - (struct ntreg_sk_record *) (h->addr + sk_next_offset); - - sk_prev->sk_next = htole32 (sk_next_offset - 0x1000); - sk_next->sk_prev = htole32 (sk_prev_offset - 0x1000); - } - - /* Refcount is zero so really delete this block. */ - mark_block_unused (h, sk_offset); - } - - return 0; -} - -/* Callback from hivex_node_delete_child which is called to delete a - * node AFTER its subnodes have been visited. The subnodes have been - * deleted but we still have to delete any lf/lh/li/ri records and the - * value list block and values, followed by deleting the node itself. - */ -static int -delete_node (hive_h *h, void *opaque, hive_node_h node, const char *name) -{ - /* Get the intermediate blocks. The subkeys have already been - * deleted by this point, so tell get_children() not to check for - * validity of the nk-records. - */ - hive_node_h *unused; - size_t *blocks; - if (get_children (h, node, &unused, &blocks, GET_CHILDREN_NO_CHECK_NK) == -1) - return -1; - free (unused); - - /* We don't care what's in these intermediate blocks, so we can just - * delete them unconditionally. - */ - size_t i; - for (i = 0; blocks[i] != 0; ++i) - mark_block_unused (h, blocks[i]); - - free (blocks); - - /* Delete the values in the node. */ - if (delete_values (h, node) == -1) - return -1; - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - - /* If the NK references an SK, delete it. */ - size_t sk_offs = le32toh (nk->sk); - if (sk_offs != 0xffffffff) { - sk_offs += 0x1000; - if (delete_sk (h, sk_offs) == -1) - return -1; - nk->sk = htole32 (0xffffffff); - } - - /* If the NK references a classname, delete it. */ - size_t cl_offs = le32toh (nk->classname); - if (cl_offs != 0xffffffff) { - cl_offs += 0x1000; - mark_block_unused (h, cl_offs); - nk->classname = htole32 (0xffffffff); - } - - /* Delete the node itself. */ - mark_block_unused (h, node); - - return 0; -} - -int -hivex_node_delete_child (hive_h *h, hive_node_h node) -{ - if (!h->writable) { - errno = EROFS; - return -1; - } - - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return -1; - } - - if (node == hivex_root (h)) { - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_delete_child: cannot delete root node\n"); - errno = EINVAL; - return -1; - } - - hive_node_h parent = hivex_node_parent (h, node); - if (parent == 0) - return -1; - - /* Delete node and all its children and values recursively. */ - static const struct hivex_visitor visitor = { .node_end = delete_node }; - if (hivex_visit_node (h, node, &visitor, sizeof visitor, NULL, 0) == -1) - return -1; - - /* Delete the link from parent to child. We need to find the lf/lh - * record which contains the offset and remove the offset from that - * record, then decrement the element count in that record, and - * decrement the overall number of subkeys stored in the parent - * node. - */ - hive_node_h *unused; - size_t *blocks; - if (get_children (h, parent, &unused, &blocks, GET_CHILDREN_NO_CHECK_NK)== -1) - return -1; - free (unused); - - size_t i, j; - for (i = 0; blocks[i] != 0; ++i) { - struct ntreg_hbin_block *block = - (struct ntreg_hbin_block *) (h->addr + blocks[i]); - - if (block->id[0] == 'l' && (block->id[1] == 'f' || block->id[1] == 'h')) { - struct ntreg_lf_record *lf = (struct ntreg_lf_record *) block; - - size_t nr_subkeys_in_lf = le16toh (lf->nr_keys); - - for (j = 0; j < nr_subkeys_in_lf; ++j) - if (le32toh (lf->keys[j].offset) + 0x1000 == node) { - for (; j < nr_subkeys_in_lf - 1; ++j) - memcpy (&lf->keys[j], &lf->keys[j+1], sizeof (lf->keys[j])); - lf->nr_keys = htole16 (nr_subkeys_in_lf - 1); - goto found; - } - } - } - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_delete_child: could not find parent to child link\n"); - errno = ENOTSUP; - return -1; - - found:; - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + parent); - size_t nr_subkeys_in_nk = le32toh (nk->nr_subkeys); - nk->nr_subkeys = htole32 (nr_subkeys_in_nk - 1); - - if (h->msglvl >= 2) - fprintf (stderr, "hivex_node_delete_child: updating nr_subkeys in parent 0x%zx to %zu\n", - parent, nr_subkeys_in_nk); - - return 0; -} - -int -hivex_node_set_values (hive_h *h, hive_node_h node, - size_t nr_values, const hive_set_value *values, - int flags) -{ - if (!h->writable) { - errno = EROFS; - return -1; - } - - if (!IS_VALID_BLOCK (h, node) || !BLOCK_ID_EQ (h, node, "nk")) { - errno = EINVAL; - return -1; - } - - /* Delete all existing values. */ - if (delete_values (h, node) == -1) - return -1; - - if (nr_values == 0) - return 0; - - /* Allocate value list node. Value lists have no id field. */ - static const char nul_id[2] = { 0, 0 }; - size_t seg_len = - sizeof (struct ntreg_value_list) + (nr_values - 1) * sizeof (uint32_t); - size_t vallist_offs = allocate_block (h, seg_len, nul_id); - if (vallist_offs == 0) - return -1; - - struct ntreg_nk_record *nk = (struct ntreg_nk_record *) (h->addr + node); - nk->nr_values = htole32 (nr_values); - nk->vallist = htole32 (vallist_offs - 0x1000); - - struct ntreg_value_list *vallist = - (struct ntreg_value_list *) (h->addr + vallist_offs); - - size_t i; - for (i = 0; i < nr_values; ++i) { - /* Allocate vk record to store this (key, value) pair. */ - static const char vk_id[2] = { 'v', 'k' }; - seg_len = sizeof (struct ntreg_vk_record) + strlen (values[i].key); - size_t vk_offs = allocate_block (h, seg_len, vk_id); - if (vk_offs == 0) - return -1; - - vallist->offset[i] = htole32 (vk_offs - 0x1000); - - struct ntreg_vk_record *vk = (struct ntreg_vk_record *) (h->addr + vk_offs); - size_t name_len = strlen (values[i].key); - vk->name_len = htole16 (name_len); - strcpy (vk->name, values[i].key); - vk->data_type = htole32 (values[i].t); - uint32_t len = values[i].len; - if (len <= 4) /* store it inline => set MSB flag */ - len |= 0x80000000; - vk->data_len = htole32 (len); - vk->flags = name_len == 0 ? 0 : 1; - - if (values[i].len <= 4) /* store it inline */ - memcpy (&vk->data_offset, values[i].value, values[i].len); - else { - size_t offs = allocate_block (h, values[i].len + 4, nul_id); - if (offs == 0) - return -1; - memcpy (h->addr + offs + 4, values[i].value, values[i].len); - vk->data_offset = htole32 (offs - 0x1000); - } - - if (name_len * 2 > le32toh (nk->max_vk_name_len)) - /* * 2 for UTF16-LE "reencoding" */ - nk->max_vk_name_len = htole32 (name_len * 2); - if (values[i].len > le32toh (nk->max_vk_data_len)) - nk->max_vk_data_len = htole32 (values[i].len); - } - - return 0; -} diff --git a/hivex/hivex.h b/hivex/hivex.h deleted file mode 100644 index f4ce8340..00000000 --- a/hivex/hivex.h +++ /dev/null @@ -1,131 +0,0 @@ -/* hivex - Windows Registry "hive" extraction library. - * Copyright (C) 2009 Red Hat Inc. - * Derived from code by Petter Nordahl-Hagen under a compatible license: - * Copyright (c) 1997-2007 Petter Nordahl-Hagen. - * Derived from code by Markus Stephany under a compatible license: - * Copyright (c)2000-2004, Markus Stephany. - * - * 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 LICENSE for the full license. - */ - -#ifndef HIVEX_H_ -#define HIVEX_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/* NOTE: This API is documented in the man page hivex(3). */ - -typedef struct hive_h hive_h; -typedef size_t hive_node_h; -typedef size_t hive_value_h; - -enum hive_type { - /* Just a key without a value. */ - hive_t_none = 0, - - /* A UTF-16 Windows string. */ - hive_t_string = 1, - - /* A UTF-16 Windows string that contains %env% (environment variable - * substitutions). - */ - hive_t_expand_string = 2, - - /* A blob of binary. */ - hive_t_binary = 3, - - /* Two ways to encode DWORDs (32 bit words). The first is little-endian. */ - hive_t_dword = 4, - hive_t_dword_be = 5, - - /* Symbolic link, we think to another part of the registry tree. */ - hive_t_link = 6, - - /* Multiple UTF-16 Windows strings, each separated by zero byte. See: - * http://blogs.msdn.com/oldnewthing/archive/2009/10/08/9904646.aspx - */ - hive_t_multiple_strings = 7, - - /* These three are unknown. */ - hive_t_resource_list = 8, - hive_t_full_resource_description = 9, - hive_t_resource_requirements_list = 10, - - /* A QWORD (64 bit word). This is stored in the file little-endian. */ - hive_t_qword = 11 -}; - -typedef enum hive_type hive_type; - -/* Bitmask of flags passed to hivex_open. */ -#define HIVEX_OPEN_VERBOSE 1 -#define HIVEX_OPEN_DEBUG 2 -#define HIVEX_OPEN_MSGLVL_MASK (HIVEX_OPEN_VERBOSE|HIVEX_OPEN_DEBUG) -#define HIVEX_OPEN_WRITE 4 - -extern hive_h *hivex_open (const char *filename, int flags); -extern int hivex_close (hive_h *h); -extern hive_node_h hivex_root (hive_h *h); -extern char *hivex_node_name (hive_h *h, hive_node_h node); -extern hive_node_h *hivex_node_children (hive_h *h, hive_node_h node); -extern hive_node_h hivex_node_get_child (hive_h *h, hive_node_h node, const char *name); -extern hive_node_h hivex_node_parent (hive_h *h, hive_node_h node); -extern hive_value_h *hivex_node_values (hive_h *h, hive_node_h node); -extern hive_value_h hivex_node_get_value (hive_h *h, hive_node_h node, const char *key); -extern char *hivex_value_key (hive_h *h, hive_value_h value); -extern int hivex_value_type (hive_h *h, hive_value_h value, hive_type *t, size_t *len); -extern char *hivex_value_value (hive_h *h, hive_value_h value, hive_type *t, size_t *len); -extern char *hivex_value_string (hive_h *h, hive_value_h value); -extern char **hivex_value_multiple_strings (hive_h *h, hive_value_h value); -extern int32_t hivex_value_dword (hive_h *h, hive_value_h value); -extern int64_t hivex_value_qword (hive_h *h, hive_value_h value); -struct hivex_visitor { - int (*node_start) (hive_h *, void *opaque, hive_node_h, const char *name); - int (*node_end) (hive_h *, void *opaque, hive_node_h, const char *name); - int (*value_string) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str); - int (*value_multiple_strings) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv); - int (*value_string_invalid_utf16) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str); - int (*value_dword) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int32_t); - int (*value_qword) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int64_t); - int (*value_binary) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); - int (*value_none) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); - int (*value_other) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); - int (*value_any) (hive_h *, void *opaque, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); -}; - -#define HIVEX_VISIT_SKIP_BAD 1 - -extern int hivex_visit (hive_h *h, const struct hivex_visitor *visitor, size_t len, void *opaque, int flags); -extern int hivex_visit_node (hive_h *h, hive_node_h node, const struct hivex_visitor *visitor, size_t len, void *opaque, int flags); - -extern int hivex_commit (hive_h *h, const char *filename, int flags); -extern hive_node_h hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name); -extern int hivex_node_delete_child (hive_h *h, hive_node_h node); - -struct hive_set_value { - char *key; - hive_type t; - size_t len; - char *value; -}; -typedef struct hive_set_value hive_set_value; - -extern int hivex_node_set_values (hive_h *h, hive_node_h node, size_t nr_values, const hive_set_value *values, int flags); - -#ifdef __cplusplus -} -#endif - -#endif /* HIVEX_H_ */ diff --git a/hivex/hivex.pod b/hivex/hivex.pod deleted file mode 100644 index 275eb423..00000000 --- a/hivex/hivex.pod +++ /dev/null @@ -1,655 +0,0 @@ -=encoding utf8 - -=head1 NAME - -hivex - Windows Registry "hive" extraction library - -=head1 SYNOPSIS - - hive_h *hivex_open (const char *filename, int flags); - int hivex_close (hive_h *h); - -=head1 DESCRIPTION - -libhivex is a library for extracting the contents of Windows Registry -"hive" files. It is designed to be secure against buggy or malicious -registry files. - -Unlike many other tools in this area, it doesn't use the textual .REG -format for output, because parsing that is as much trouble as parsing -the original binary format. Instead it makes the file available -through a C API, or there is a separate program to export the hive as -XML (see L<hivexml(1)>), or to get individual keys (see -L<hivexget(1)>). - -=head2 OPENING AND CLOSING A HIVE - -=over 4 - -=item hive_h *hivex_open (const char *filename, int flags); - -Opens the hive named C<filename> for reading. - -Flags is an ORed list of the open flags (or C<0> if you don't -want to pass any flags). These flags are defined: - -=over 4 - -=item HIVEX_OPEN_VERBOSE - -Verbose messages. - -=item HIVEX_OPEN_DEBUG - -Very verbose messages, suitable for debugging problems in the library -itself. - -This is also selected if the C<HIVEX_DEBUG> environment variable -is set to 1. - -=item HIVEX_OPEN_WRITE - -Open the hive for writing. If omitted, the hive is read-only. - -See L</WRITING TO HIVE FILES>. - -=back - -C<hivex_open> returns a hive handle. On error this returns NULL and -sets C<errno> to indicate the error. - -=item int hivex_close (hive_h *h); - -Close a hive handle and free all associated resources. - -Note that any uncommitted writes are I<not> committed by this call, -but instead are lost. See L</WRITING TO HIVE FILES>. - -Returns 0 on success. On error this returns -1 and sets errno. - -=back - -=head2 NAVIGATING THE TREE OF HIVE SUBKEYS - -=over 4 - -=item hive_node_h - -This is a node handle, an integer but opaque outside the library. -Valid node handles cannot be 0. The library returns 0 in some -situations to indicate an error. - -=item hive_node_h hivex_root (hive_h *h); - -Return root node of the hive. All valid registries must contain -a root node. - -On error this returns 0 and sets errno. - -=item char *hivex_node_name (hive_h *h, hive_node_h node); - -Return the name of the node. The name is reencoded as UTF-8 -and returned as a C string. - -The string should be freed by the caller when it is no longer needed. - -Note that the name of the root node is a dummy, such as -C<$$$PROTO.HIV> (other names are possible: it seems to depend on the -tool or program that created the hive in the first place). You can -only know the "real" name of the root node by knowing which registry -file this hive originally comes from, which is knowledge that is -outside the scope of this library. - -On error this returns NULL and sets errno. - -=item hive_node_h *hivex_node_children (hive_h *h, hive_node_h node); - -Return a 0-terminated array of nodes which are the subkeys -(children) of C<node>. - -The array should be freed by the caller when it is no longer needed. - -On error this returns NULL and sets errno. - -=item hive_node_h hivex_node_get_child (hive_h *h, hive_node_h node, const char *name); - -Return the child of node with the name C<name>, if it exists. - -The name is matched case insensitively. - -If the child node does not exist, this returns 0 without -setting errno. - -On error this returns 0 and sets errno. - -=item hive_node_h hivex_node_parent (hive_h *h, hive_node_h node); - -Return the parent of C<node>. - -On error this returns 0 and sets errno. - -The parent pointer of the root node in registry files that we -have examined seems to be invalid, and so this function will -return an error if called on the root node. - -=back - -=head2 GETTING VALUES AT A NODE - -The enum below describes the possible types for the value(s) -stored at each node. - - enum hive_type { - hive_t_none = 0, - hive_t_string = 1, - hive_t_expand_string = 2, - hive_t_binary = 3, - hive_t_dword = 4, - hive_t_dword_be = 5, - hive_t_link = 6, - hive_t_multiple_strings = 7, - hive_t_resource_list = 8, - hive_t_full_resource_description = 9, - hive_t_resource_requirements_list = 10, - hive_t_qword = 11 - }; - -=over 4 - -=item hive_value_h - -This is a value handle, an integer but opaque outside the library. -Valid value handles cannot be 0. The library returns 0 in some -situations to indicate an error. - -=item hive_value_h *hivex_node_values (hive_h *h, hive_node_h node); - -Return the 0-terminated array of (key, value) pairs attached to -this node. - -The array should be freed by the caller when it is no longer needed. - -On error this returns NULL and sets errno. - -=item hive_value_h hivex_node_get_value (hive_h *h, hive_node_h node, const char *key); - -Return the value attached to this node which has the name C<key>, -if it exists. - -The key name is matched case insensitively. - -Note that to get the default key, you should pass the empty -string C<""> here. The default key is often written C<"@">, but -inside hives that has no meaning and won't give you the -default key. - -If no such key exists, this returns 0 and does not set errno. - -On error this returns 0 and sets errno. - -=item char *hivex_value_key (hive_h *h, hive_value_h value); - -Return the key (name) of a (key, value) pair. The name -is reencoded as UTF-8 and returned as a C string. - -The string should be freed by the caller when it is no longer needed. - -Note that this function can return a zero-length string. In the -context of Windows Registries, this means that this value is the -default key for this node in the tree. This is usually written -as C<"@">. - -On error this returns NULL and sets errno. - -=item int hivex_value_type (hive_h *h, hive_value_h value, hive_type *t, size_t *len); - -Return the data type and length of the value in this (key, value) -pair. See also C<hivex_value_value> which returns all this -information, and the value itself. Also, C<hivex_value_*> functions -below which can be used to return the value in a more useful form when -you know the type in advance. - -Returns 0 on success. On error this returns -1 and sets errno. - -=item char *hivex_value_value (hive_h *h, hive_value_h value, hive_type *t, size_t *len); - -Return the value of this (key, value) pair. The value should -be interpreted according to its type (see C<enum hive_type>). - -The value is returned in an array of bytes of length C<len>. - -The value should be freed by the caller when it is no longer needed. - -On error this returns NULL and sets errno. - -=item char *hivex_value_string (hive_h *h, hive_value_h value); - -If this value is a string, return the string reencoded as UTF-8 -(as a C string). This only works for values which have type -C<hive_t_string>, C<hive_t_expand_string> or C<hive_t_link>. - -The string should be freed by the caller when it is no longer needed. - -On error this returns NULL and sets errno. - -=item char **hivex_value_multiple_strings (hive_h *h, hive_value_h value); - -If this value is a multiple-string, return the strings reencoded -as UTF-8 (as a NULL-terminated array of C strings). This only -works for values which have type C<hive_t_multiple_strings>. - -The string array and each string in it should be freed by the -caller when they are no longer needed. - -On error this returns NULL and sets errno. - -=item int32_t hivex_value_dword (hive_h *h, hive_value_h value); - -If this value is a DWORD (Windows int32), return it. This only works -for values which have type C<hive_t_dword> or C<hive_t_dword_be>. - -=item int64_t hivex_value_qword (hive_h *h, hive_value_h value); - -If this value is a QWORD (Windows int64), return it. This only -works for values which have type C<hive_t_qword>. - -=back - -=head2 VISITING ALL NODES - -The visitor pattern is useful if you want to visit all nodes -in the tree or all nodes below a certain point in the tree. - -First you set up your own C<struct hivex_visitor> with your -callback functions. - -Each of these callback functions should return 0 on success or -1 -on error. If any callback returns -1, then the entire visit -terminates immediately. If you don't need a callback function at -all, set the function pointer to NULL. - - struct hivex_visitor { - int (*node_start) (hive_h *, void *opaque, hive_node_h, const char *name); - int (*node_end) (hive_h *, void *opaque, hive_node_h, const char *name); - int (*value_string) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, const char *str); - int (*value_multiple_strings) (hive_h *, void *opaque, hive_node_h, - hive_value_h, hive_type t, size_t len, const char *key, char **argv); - int (*value_string_invalid_utf16) (hive_h *, void *opaque, hive_node_h, - hive_value_h, hive_type t, size_t len, const char *key, - const char *str); - int (*value_dword) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, int32_t); - int (*value_qword) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, int64_t); - int (*value_binary) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, const char *value); - int (*value_none) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, const char *value); - int (*value_other) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, const char *value); - /* If value_any callback is not NULL, then the other value_* - * callbacks are not used, and value_any is called on all values. - */ - int (*value_any) (hive_h *, void *opaque, hive_node_h, hive_value_h, - hive_type t, size_t len, const char *key, const char *value); - }; - -=over 4 - -=item int hivex_visit (hive_h *h, const struct hivex_visitor *visitor, size_t len, void *opaque, int flags); - -Visit all the nodes recursively in the hive C<h>. - -C<visitor> should be a C<hivex_visitor> structure with callback -fields filled in as required (unwanted callbacks can be set to -NULL). C<len> must be the length of the 'visitor' struct (you -should pass C<sizeof (struct hivex_visitor)> for this). - -This returns 0 if the whole recursive visit was completed -successfully. On error this returns -1. If one of the callback -functions returned an error than we don't touch errno. If the -error was generated internally then we set errno. - -You can skip bad registry entries by setting C<flag> to -C<HIVEX_VISIT_SKIP_BAD>. If this flag is not set, then a bad registry -causes the function to return an error immediately. - -This function is robust if the registry contains cycles or -pointers which are invalid or outside the registry. It detects -these cases and returns an error. - -=item int hivex_visit_node (hive_h *h, hive_node_h node, const struct hivex_visitor *visitor, size_t len, void *opaque); - -Same as C<hivex_visit> but instead of starting out at the root, this -starts at C<node>. - -=back - -=head2 WRITING TO HIVE FILES - -The hivex library supports making limited modifications to hive files. -We have tried to implement this very conservatively in order to reduce -the chance of corrupting your registry. However you should be careful -and take back-ups, since Microsoft has never documented the hive -format, and so it is possible there are nuances in the -reverse-engineered format that we do not understand. - -To be able to modify a hive, you must pass the C<HIVEX_OPEN_WRITE> -flag to C<hivex_open>, otherwise any write operation will return with -errno C<EROFS>. - -The write operations shown below do not modify the on-disk file -immediately. You must call C<hivex_commit> in order to write the -changes to disk. If you call C<hivex_close> without committing then -any writes are discarded. - -Hive files internally consist of a "memory dump" of binary blocks -(like the C heap), and some of these blocks can be unused. The hivex -library never reuses these unused blocks. Instead, to ensure -robustness in the face of the partially understood on-disk format, -hivex only allocates new blocks after the end of the file, and makes -minimal modifications to existing structures in the file to point to -these new blocks. This makes hivex slightly less disk-efficient than -it could be, but disk is cheap, and registry modifications tend to be -very small. - -When deleting nodes, it is possible that this library may leave -unreachable live blocks in the hive. This is because certain parts of -the hive disk format such as security (sk) records and big data (db) -records and classname fields are not well understood (and not -documented at all) and we play it safe by not attempting to modify -them. Apart from wasting a little bit of disk space, it is not -thought that unreachable blocks are a problem. - -=over 4 - -=item int hivex_commit (hive_h *h, const char *filename, int flags); - -Commit (write) any changes which have been made. - -C<filename> is the new file to write. If C<filename == NULL> then we -overwrite the original file (ie. the file name that was passed to -C<hivex_open>). C<flags> is not used, always pass 0. - -Returns 0 on success. On error this returns -1 and sets errno. - -Note this does not close the hive handle. You can perform further -operations on the hive after committing, including making more -modifications. If you no longer wish to use the hive, call -C<hivex_close> after this. - -=item hive_node_h hivex_node_add_child (hive_h *h, hive_node_h parent, const char *name); - -Add a new child node named C<name> to the existing node C<parent>. -The new child initially has no subnodes and contains no keys or -values. The sk-record (security descriptor) is inherited from -the parent. - -The parent must not have an existing child called C<name>, so if you -want to overwrite an existing child, call C<hivex_node_delete_child> -first. - -Returns the node handle. On error this returns 0 and sets errno. - -=item int hivex_node_delete_child (hive_h *h, hive_node_h node); - -Delete the node C<node>. All values at the node and all subnodes are -deleted (recursively). The C<node> handle and the handles of all -subnodes become invalid. You cannot delete the root node. - -Returns 0 on success. On error this returns -1 and sets errno. - -=item hive_set_value - -The typedef C<hive_set_value> is used in conjunction with the -C<hivex_node_set_values> call described below. - - struct hive_set_value { - char *key; /* key - a UTF-8 encoded ASCIIZ string */ - hive_type t; /* type of value field */ - size_t len; /* length of value field in bytes */ - char *value; /* value field */ - }; - typedef struct hive_set_value hive_set_value; - -To set the default value for a node, you have to pass C<key = "">. - -Note that the C<value> field is just treated as a list of bytes, and -is stored directly in the hive. The caller has to ensure correct -encoding and endianness, for example converting dwords to little -endian. - -The correct type and encoding for values depends on the node and key -in the registry, the version of Windows, and sometimes even changes -between versions of Windows for the same key. We don't document it -here. Often it's not documented at all. - -=item int hivex_node_set_values (hive_h *h, hive_node_h node, size_t nr_values, const hive_set_value *values, int flags); - -This call can be used to set all the (key, value) pairs stored in C<node>. - -C<node> is the node to modify. C<values> is an array of (key, value) -pairs. There should be C<nr_values> elements in this array. C<flags> -is not used, always pass 0. - -Any existing values stored at the node are discarded, and their -C<hive_value_h> handles become invalid. Thus you can remove all -values stored at C<node> by passing C<nr_values = 0>. - -Returns 0 on success. On error this returns -1 and sets errno. - -Note that this library does not offer a way to modify just a single -key at a node. We don't implement a way to do this efficiently. - -=back - -=head3 WRITE OPERATIONS WHICH ARE NOT SUPPORTED - -=over 4 - -=item * - -Changing the root node. - -=item * - -Creating a new hive file from scratch. This is impossible at present -because not all fields in the header are understood. - -=item * - -Modifying or deleting single values at a node. - -=item * - -Modifying security key (sk) records or classnames. -Previously we did not understand these records. However now they -are well-understood and we could add support if it was required -(but nothing much really uses them). - -=back - -=head1 THE STRUCTURE OF THE WINDOWS REGISTRY - -Note: To understand the relationship between hives and the common -Windows Registry keys (like C<HKEY_LOCAL_MACHINE>) please see the -Wikipedia page on the Windows Registry. - -The Windows Registry is split across various binary files, each -file being known as a "hive". This library only handles a single -hive file at a time. - -Hives are n-ary trees with a single root. Each node in the tree -has a name. - -Each node in the tree (including non-leaf nodes) may have an -arbitrary list of (key, value) pairs attached to it. It may -be the case that one of these pairs has an empty key. This -is referred to as the default key for the node. - -The (key, value) pairs are the place where the useful data is -stored in the registry. The key is always a string (possibly the -empty string for the default key). The value is a typed object -(eg. string, int32, binary, etc.). - -=head2 RELATIONSHIP TO .REG FILES - -Although this library does not care about or deal with Windows reg -files, it's useful to look at the relationship between the registry -itself and reg files because they are so common. - -A reg file is a text representation of the registry, or part of the -registry. The actual registry hives that Windows uses are binary -files. There are a number of Windows and Linux tools that let you -generate reg files, or merge reg files back into the registry hives. -Notable amongst them is Microsoft's REGEDIT program (formerly known as -REGEDT32). - -A typical reg file will contain many sections looking like this: - - [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Stack] - "@"="Generic Stack" - "TileInfo"="prop:System.FileCount" - "TilePath"=str(2):"%systemroot%\\system32" - "ThumbnailCutoff"=dword:00000000 - "FriendlyTypeName"=hex(2):40,00,25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,\ - 6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,\ - 33,00,32,00,5c,00,73,00,65,00,61,00,72,00,63,00,68,00,66,00,\ - 6f,00,6c,00,64,00,65,00,72,00,2e,00,64,00,6c,00,6c,00,2c,00,\ - 2d,00,39,00,30,00,32,00,38,00,00,00,d8 - -Taking this one piece at a time: - - [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Stack] - -This is the path to this node in the registry tree. The first part, -C<HKEY_LOCAL_MACHINE\SOFTWARE> means that this comes from a hive -(file) called C<SOFTWARE>. C<\Classes\Stack> is the real path part, -starting at the root node of the C<SOFTWARE> hive. - -Below the node name is a list of zero or more key-value pairs. Any -interior or leaf node in the registry may have key-value pairs -attached. - - "@"="Generic Stack" - -This is the "default key". In reality (ie. inside the binary hive) -the key string is the empty string. In reg files this is written as -C<@> but this has no meaning either in the hives themselves or in this -library. The value is a string (type 1 - see C<enum hive_type> -above). - - "TileInfo"="prop:System.FileCount" - -This is a regular (key, value) pair, with the value being a type 1 -string. Note that inside the binary file the string is likely to be -UTF-16 encoded. This library converts to and from UTF-8 strings -transparently. - - "TilePath"=str(2):"%systemroot%\\system32" - -The value in this case has type 2 (expanded string) meaning that some -%...% variables get expanded by Windows. (This library doesn't know -or care about variable expansion). - - "ThumbnailCutoff"=dword:00000000 - -The value in this case is a dword (type 4). - - "FriendlyTypeName"=hex(2):40,00,.... - -This value is an expanded string (type 2) represented in the reg file -as a series of hex bytes. In this case the string appears to be a -UTF-16 string. - -=head1 NOTE ON THE USE OF ERRNO - -Many functions in this library set errno to indicate errors. These -are the values of errno you may encounter (this list is not -exhaustive): - -=over 4 - -=item ENOTSUP - -Corrupt or unsupported Registry file format. - -=item ENOKEY - -Missing root key. - -=item EINVAL - -Passed an invalid argument to the function. - -=item EFAULT - -Followed a Registry pointer which goes outside -the registry or outside a registry block. - -=item ELOOP - -Registry contains cycles. - -=item ERANGE - -Field in the registry out of range. - -=item EEXIST - -Registry key already exists. - -=item EROFS - -Tried to write to a registry which is not opened for writing. - -=back - -=head1 ENVIRONMENT VARIABLES - -=over 4 - -=item HIVEX_DEBUG - -Setting HIVEX_DEBUG=1 will enable very verbose messages. This is -useful for debugging problems with the library itself. - -=back - -=head1 SEE ALSO - -L<hivexml(1)>, -L<hivexget(1)>, -L<virt-win-reg(1)>, -L<guestfs(3)>, -L<http://libguestfs.org/>, -L<virt-cat(1)>, -L<virt-edit(1)>, -L<http://en.wikipedia.org/wiki/Windows_Registry>. - -=head1 AUTHORS - -Richard W.M. Jones (C<rjones at redhat dot com>) - -=head1 COPYRIGHT - -Copyright (C) 2009-2010 Red Hat Inc. - -Derived from code by Petter Nordahl-Hagen under a compatible license: -Copyright (C) 1997-2007 Petter Nordahl-Hagen. - -Derived from code by Markus Stephany under a compatible license: -Copyright (C) 2000-2004 Markus Stephany. - -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 LICENSE for the full license. diff --git a/hivex/hivexget b/hivex/hivexget deleted file mode 100755 index f804d0d3..00000000 --- a/hivex/hivexget +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2009-2010 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -set -e - -if [ $# -lt 2 -o $# -gt 3 ]; then - echo "hivexget hivefile path [key]" - exit 1 -fi - -if [ $# -eq 2 ]; then - hivexsh <<EOF -load $1 -cd $2 -lsval -exit -EOF -else - key=$3 - if [ "$key" = "" ]; then - key="@" - fi - hivexsh <<EOF -load $1 -cd $2 -lsval $key -exit -EOF -fi diff --git a/hivex/hivexget.pod b/hivex/hivexget.pod deleted file mode 100644 index 4fbac132..00000000 --- a/hivex/hivexget.pod +++ /dev/null @@ -1,95 +0,0 @@ -=encoding utf8 - -=head1 NAME - -hivexget - Get subkey from a Windows Registry binary "hive" file - -=head1 SYNOPSIS - - hivexget hivefile '\Path\To\SubKey' - - hivexget hivefile '\Path\To\SubKey' name - -=head1 DESCRIPTION - -I<Note:> This is a low-level tool. For a more convenient way to -navigate the Windows Registry in Windows virtual machines, see -L<virt-win-reg(1)>. - -This program navigates through a Windows Registry binary "hive" -file and extracts I<either> all the (key, value) data pairs -stored in that subkey I<or> just the single named data item. - -In the first form: - - hivexget hivefile '\Path\To\SubKey' - -C<hivefile> is some Windows Registry binary hive, and C<\Path\To\Subkey> -is a path within that hive. I<NB> the path is relative to the top -of this hive, and is I<not> the full path as you would use in Windows -(eg. C<\HKEY_LOCAL_MACHINE> is not a valid path). - -If the subkey exists, then the output lists all data pairs under this -subkey, in a format compatible with C<regedit> in Windows. - -In the second form: - - hivexget hivefile '\Path\To\SubKey' name - -C<hivefile> and path are as above. C<name> is the name of the value -of interest (use C<@> for the default value). - -The corresponding data item is printed "raw" (ie. no processing or -escaping) except: - -=over 4 - -=item 1 - -If it's a string we will convert it from Windows UTF-16 to UTF-8, if -this conversion is possible. The string is printed with a single -trailing newline. - -=item 2 - -If it's a multiple-string value, each string is printed on a separate -line. - -=item 3 - -If it's a numeric value, it is printed as a decimal number. - -=back - -=head1 SEE ALSO - -L<hivex(3)>, -L<hivexml(1)>, -L<hivexsh(1)>, -L<virt-win-reg(1)>, -L<guestfs(3)>, -L<http://libguestfs.org/>, -L<virt-cat(1)>, -L<virt-edit(1)>. - -=head1 AUTHORS - -Richard W.M. Jones (C<rjones at redhat dot com>) - -=head1 COPYRIGHT - -Copyright (C) 2009 Red Hat Inc. - -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. diff --git a/hivex/hivexml.c b/hivex/hivexml.c deleted file mode 100644 index 90cb22bd..00000000 --- a/hivex/hivexml.c +++ /dev/null @@ -1,345 +0,0 @@ -/* hivexml - Convert Windows Registry "hive" to XML file. - * Copyright (C) 2009 Red Hat Inc. - * - * 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. - */ - -#include <config.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <inttypes.h> -#include <unistd.h> -#include <errno.h> - -#include <libxml/xmlwriter.h> - -#include "hivex.h" - -#ifdef HAVE_GETTEXT -#include "gettext.h" -#define _(str) dgettext(PACKAGE, (str)) -//#define N_(str) dgettext(PACKAGE, (str)) -#else -#define _(str) str -//#define N_(str) str -#endif - -/* Callback functions. */ -static int node_start (hive_h *, void *, hive_node_h, const char *name); -static int node_end (hive_h *, void *, hive_node_h, const char *name); -static int value_string (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str); -static int value_multiple_strings (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv); -static int value_string_invalid_utf16 (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str); -static int value_dword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int32_t); -static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int64_t); -static int value_binary (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); -static int value_none (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); -static int value_other (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value); - -static struct hivex_visitor visitor = { - .node_start = node_start, - .node_end = node_end, - .value_string = value_string, - .value_multiple_strings = value_multiple_strings, - .value_string_invalid_utf16 = value_string_invalid_utf16, - .value_dword = value_dword, - .value_qword = value_qword, - .value_binary = value_binary, - .value_none = value_none, - .value_other = value_other -}; - -#define XML_CHECK(proc, args) \ - do { \ - if ((proc args) == -1) { \ - fprintf (stderr, _("%s: failed to write XML document\n"), #proc); \ - exit (EXIT_FAILURE); \ - } \ - } while (0) - -int -main (int argc, char *argv[]) -{ - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEBASEDIR); - textdomain (PACKAGE); - - int c; - int open_flags = 0; - int visit_flags = 0; - - while ((c = getopt (argc, argv, "dk")) != EOF) { - switch (c) { - case 'd': - open_flags |= HIVEX_OPEN_DEBUG; - break; - case 'k': - visit_flags |= HIVEX_VISIT_SKIP_BAD; - break; - default: - fprintf (stderr, "hivexml [-dk] regfile > output.xml\n"); - exit (EXIT_FAILURE); - } - } - - if (optind + 1 != argc) { - fprintf (stderr, _("hivexml: missing name of input file\n")); - exit (EXIT_FAILURE); - } - - hive_h *h = hivex_open (argv[optind], open_flags); - if (h == NULL) { - perror (argv[optind]); - exit (EXIT_FAILURE); - } - - /* Note both this macro, and xmlTextWriterStartDocument leak memory. There - * doesn't seem to be any way to recover that memory, but it's not a - * large amount. - */ - LIBXML_TEST_VERSION; - - xmlTextWriterPtr writer; - writer = xmlNewTextWriterFilename ("/dev/stdout", 0); - if (writer == NULL) { - fprintf (stderr, _("xmlNewTextWriterFilename: failed to create XML writer\n")); - exit (EXIT_FAILURE); - } - - XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL)); - XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive")); - - if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) { - perror (argv[optind]); - exit (EXIT_FAILURE); - } - - if (hivex_close (h) == -1) { - perror (argv[optind]); - exit (EXIT_FAILURE); - } - - XML_CHECK (xmlTextWriterEndElement, (writer)); - XML_CHECK (xmlTextWriterEndDocument, (writer)); - xmlFreeTextWriter (writer); - - exit (EXIT_SUCCESS); -} - -static int -node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node")); - XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name)); - return 0; -} - -static int -node_end (hive_h *h, void *writer_v, hive_node_h node, const char *name) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - XML_CHECK (xmlTextWriterEndElement, (writer)); - return 0; -} - -static void -start_value (xmlTextWriterPtr writer, - const char *key, const char *type, const char *encoding) -{ - XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "value")); - XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "type", BAD_CAST type)); - if (encoding) - XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "encoding", BAD_CAST encoding)); - if (*key) - XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "key", BAD_CAST key)); - else /* default key */ - XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "default", BAD_CAST "1")); -} - -static void -end_value (xmlTextWriterPtr writer) -{ - XML_CHECK (xmlTextWriterEndElement, (writer)); -} - -static int -value_string (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value, - hive_type t, size_t len, const char *key, const char *str) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - const char *type; - - switch (t) { - case hive_t_string: type = "string"; break; - case hive_t_expand_string: type = "expand"; break; - case hive_t_link: type = "link"; break; - - case hive_t_none: - case hive_t_binary: - case hive_t_dword: - case hive_t_dword_be: - case hive_t_multiple_strings: - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - case hive_t_qword: - abort (); /* internal error - should not happen */ - - default: - type = "unknown"; - } - - start_value (writer, key, type, NULL); - XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST str)); - end_value (writer); - return 0; -} - -static int -value_multiple_strings (hive_h *h, void *writer_v, hive_node_h node, - hive_value_h value, hive_type t, size_t len, - const char *key, char **argv) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - start_value (writer, key, "string-list", NULL); - - size_t i; - for (i = 0; argv[i] != NULL; ++i) { - XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "string")); - XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST argv[i])); - XML_CHECK (xmlTextWriterEndElement, (writer)); - } - - end_value (writer); - return 0; -} - -static int -value_string_invalid_utf16 (hive_h *h, void *writer_v, hive_node_h node, - hive_value_h value, hive_type t, size_t len, - const char *key, - const char *str /* original data */) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - const char *type; - - switch (t) { - case hive_t_string: type = "bad-string"; break; - case hive_t_expand_string: type = "bad-expand"; break; - case hive_t_link: type = "bad-link"; break; - case hive_t_multiple_strings: type = "bad-string-list"; break; - - case hive_t_none: - case hive_t_binary: - case hive_t_dword: - case hive_t_dword_be: - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - case hive_t_qword: - abort (); /* internal error - should not happen */ - - default: - type = "unknown"; - } - - start_value (writer, key, type, "base64"); - XML_CHECK (xmlTextWriterWriteBase64, (writer, str, 0, len)); - end_value (writer); - - return 0; -} - -static int -value_dword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value, - hive_type t, size_t len, const char *key, int32_t v) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - start_value (writer, key, "int32", NULL); - XML_CHECK (xmlTextWriterWriteFormatString, (writer, "%" PRIi32, v)); - end_value (writer); - return 0; -} - -static int -value_qword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value, - hive_type t, size_t len, const char *key, int64_t v) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - start_value (writer, key, "int64", NULL); - XML_CHECK (xmlTextWriterWriteFormatString, (writer, "%" PRIi64, v)); - end_value (writer); - return 0; -} - -static int -value_binary (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value, - hive_type t, size_t len, const char *key, const char *v) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - start_value (writer, key, "binary", "base64"); - XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len)); - end_value (writer); - return 0; -} - -static int -value_none (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value, - hive_type t, size_t len, const char *key, const char *v) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - start_value (writer, key, "none", "base64"); - if (len > 0) XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len)); - end_value (writer); - return 0; -} - -static int -value_other (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value, - hive_type t, size_t len, const char *key, const char *v) -{ - xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v; - const char *type; - - switch (t) { - case hive_t_none: - case hive_t_binary: - case hive_t_dword: - case hive_t_dword_be: - case hive_t_qword: - case hive_t_string: - case hive_t_expand_string: - case hive_t_link: - case hive_t_multiple_strings: - abort (); /* internal error - should not happen */ - - case hive_t_resource_list: type = "resource-list"; break; - case hive_t_full_resource_description: type = "resource-description"; break; - case hive_t_resource_requirements_list: type = "resource-requirements"; break; - - default: - type = "unknown"; - } - - start_value (writer, key, type, "base64"); - if (len > 0) XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len)); - end_value (writer); - - return 0; -} diff --git a/hivex/hivexml.pod b/hivex/hivexml.pod deleted file mode 100644 index d6a87b4e..00000000 --- a/hivex/hivexml.pod +++ /dev/null @@ -1,65 +0,0 @@ -=encoding utf8 - -=head1 NAME - -hivexml - Convert Windows Registry binary "hive" into XML - -=head1 SYNOPSIS - - hivexml [-dk] hivefile > output.xml - -=head1 DESCRIPTION - -This program converts a single Windows Registry binary "hive" -file into a self-describing XML format. - -=head1 OPTIONS - -=over 4 - -=item B<-d> - -Enable lots of debug messages. If you find a Registry file -that this program cannot parse, please enable this option and -post the complete output I<and> the Registry file in your -bug report. - -=item B<-k> - -Keep going even if we find errors in the Registry file. This -skips over any parts of the Registry that we cannot read. - -=back - -=head1 SEE ALSO - -L<hivex(3)>, -L<hivexget(1)>, -L<hivexsh(1)>, -L<virt-win-reg(1)>, -L<guestfs(3)>, -L<http://libguestfs.org/>, -L<virt-cat(1)>, -L<virt-edit(1)>. - -=head1 AUTHORS - -Richard W.M. Jones (C<rjones at redhat dot com>) - -=head1 COPYRIGHT - -Copyright (C) 2009 Red Hat Inc. - -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. diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c deleted file mode 100644 index 332b7739..00000000 --- a/hivex/hivexsh.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* hivexsh - Hive shell. - * Copyright (C) 2009 Red Hat Inc. - * - * 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. - */ - -#include <config.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <inttypes.h> -#include <fcntl.h> -#include <unistd.h> -#include <assert.h> -#include <errno.h> - -#ifdef HAVE_LIBREADLINE -#include <readline/readline.h> -#include <readline/history.h> -#endif - -#ifdef HAVE_GETTEXT -#include "gettext.h" -#define _(str) dgettext(PACKAGE, (str)) -//#define N_(str) dgettext(PACKAGE, (str)) -#else -#define _(str) str -//#define N_(str) str -#endif - -#define STREQ(a,b) (strcmp((a),(b)) == 0) -#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) -#define STRNEQ(a,b) (strcmp((a),(b)) != 0) -//#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) -//#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) -//#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) -//#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) -//#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) -#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0) - -#include "c-ctype.h" -#include "xstrtol.h" - -#include "hivex.h" -#include "byte_conversions.h" - -#define HIVEX_MAX_VALUES 1000 - -static int quit = 0; -static int is_tty; -static hive_h *h = NULL; -static char *prompt_string = NULL; /* Normal prompt string. */ -static char *loaded = NULL; /* Basename of loaded file, if any. */ -static hive_node_h cwd; /* Current node. */ -static int open_flags = 0; /* Flags used when loading a hive file. */ - -static void usage (void) __attribute__((noreturn)); -static void print_node_path (hive_node_h, FILE *); -static void set_prompt_string (void); -static void initialize_readline (void); -static void cleanup_readline (void); -static void add_history_line (const char *); -static char *rl_gets (const char *prompt_string); -static void sort_strings (char **strings, int len); -static int get_xdigit (char c); -static int dispatch (char *cmd, char *args); -static int cmd_add (char *name); -static int cmd_cd (char *path); -static int cmd_close (char *path); -static int cmd_commit (char *path); -static int cmd_del (char *args); -static int cmd_help (char *args); -static int cmd_load (char *hivefile); -static int cmd_ls (char *args); -static int cmd_lsval (char *args); -static int cmd_setval (char *args); - -static void -usage (void) -{ - fprintf (stderr, "hivexsh [-dfw] [hivefile]\n"); - exit (EXIT_FAILURE); -} - -int -main (int argc, char *argv[]) -{ - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEBASEDIR); - textdomain (PACKAGE); - - int c; - const char *filename = NULL; - - set_prompt_string (); - - while ((c = getopt (argc, argv, "dfw")) != EOF) { - switch (c) { - case 'd': - open_flags |= HIVEX_OPEN_DEBUG; - break; - case 'f': - filename = optarg; - break; - case 'w': - open_flags |= HIVEX_OPEN_WRITE; - break; - default: - usage (); - } - } - - if (optind < argc) { - if (optind + 1 != argc) - usage (); - if (cmd_load (argv[optind]) == -1) - exit (EXIT_FAILURE); - } - - /* -f filename parameter */ - if (filename) { - close (0); - if (open (filename, O_RDONLY) == -1) { - perror (filename); - exit (EXIT_FAILURE); - } - } - - /* Main loop. */ - is_tty = isatty (0); - initialize_readline (); - - if (is_tty) - printf (_( -"\n" -"Welcome to hivexsh, the hivex interactive shell for examining\n" -"Windows Registry binary hive files.\n" -"\n" -"Type: 'help' for help summary\n" -" 'quit' to quit the shell\n" -"\n")); - - while (!quit) { - char *buf = rl_gets (prompt_string); - if (!buf) { - quit = 1; - if (is_tty) - printf ("\n"); - break; - } - - while (*buf && c_isspace (*buf)) - buf++; - - /* Ignore blank line. */ - if (!*buf) continue; - - /* If the next character is '#' then this is a comment. */ - if (*buf == '#') continue; - - /* Parsing is very simple - much simpler than guestfish. This is - * because Registry keys often contain spaces, and we don't want - * to bother with quoting. Therefore here we just split at the - * first whitespace into "cmd<whitespace>arg(s)". We let the - * command decide how to deal with arg(s), if at all. - */ - size_t len = strcspn (buf, " \t"); - - if (len == 0) continue; - - char *cmd = buf; - char *args; - - if (buf[len] == '\0') { - /* This is mostly safe. Although the cmd_* functions do sometimes - * modify args, then shouldn't do so when args is "". - */ - args = (char *) ""; - goto got_command; - } - - buf[len] = '\0'; - args = buf + len + 1 + strspn (&buf[len+1], " \t"); - - len = strlen (args); - while (len > 0 && c_isspace (args[len-1])) { - args[len-1] = '\0'; - len--; - } - - got_command: - /*printf ("command: '%s' args: '%s'\n", cmd, args)*/; - int r = dispatch (cmd, args); - if (!is_tty && r == -1) - exit (EXIT_FAILURE); - } - - cleanup_readline (); - free (prompt_string); - free (loaded); - if (h) hivex_close (h); - exit (0); -} - -/* Set the prompt string. This is called whenever it could change, eg. - * after loading a file or changing directory. - */ -static void -set_prompt_string (void) -{ - free (prompt_string); - prompt_string = NULL; - - FILE *fp; - char *ptr; - size_t size; - fp = open_memstream (&ptr, &size); - if (fp == NULL) { - perror ("open_memstream"); - exit (EXIT_FAILURE); - } - - if (h) { - assert (loaded != NULL); - assert (cwd != 0); - - fputs (loaded, fp); - print_node_path (cwd, fp); - } - - fprintf (fp, "> "); - fclose (fp); - prompt_string = ptr; -} - -/* Print the \full\path of a node. */ -static void -print_node_path (hive_node_h node, FILE *fp) -{ - hive_node_h root = hivex_root (h); - - if (node == root) { - fputc ('\\', fp); - return; - } - - hive_node_h parent = hivex_node_parent (h, node); - if (parent == 0) { - fprintf (stderr, _("hivexsh: error getting parent of node %zu\n"), node); - return; - } - print_node_path (parent, fp); - - if (parent != root) - fputc ('\\', fp); - - char *name = hivex_node_name (h, node); - if (name == NULL) { - fprintf (stderr, _("hivexsh: error getting node name of node %zx\n"), node); - return; - } - - fputs (name, fp); - free (name); -} - -static char *line_read = NULL; - -static char * -rl_gets (const char *prompt_string) -{ -#ifdef HAVE_LIBREADLINE - - if (is_tty) { - if (line_read) { - free (line_read); - line_read = NULL; - } - - line_read = readline (prompt_string); - - if (line_read && *line_read) - add_history_line (line_read); - - return line_read; - } - -#endif /* HAVE_LIBREADLINE */ - - static char buf[8192]; - int len; - - if (is_tty) - printf ("%s", prompt_string); - line_read = fgets (buf, sizeof buf, stdin); - - if (line_read) { - len = strlen (line_read); - if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0'; - } - - return line_read; -} - -#ifdef HAVE_LIBREADLINE -static char histfile[1024]; -static int nr_history_lines = 0; -#endif - -static void -initialize_readline (void) -{ -#ifdef HAVE_LIBREADLINE - const char *home; - - home = getenv ("HOME"); - if (home) { - snprintf (histfile, sizeof histfile, "%s/.hivexsh", home); - using_history (); - (void) read_history (histfile); - } - - rl_readline_name = "hivexsh"; -#endif -} - -static void -cleanup_readline (void) -{ -#ifdef HAVE_LIBREADLINE - int fd; - - if (histfile[0] != '\0') { - fd = open (histfile, O_WRONLY|O_CREAT, 0644); - if (fd == -1) { - perror (histfile); - return; - } - close (fd); - - (void) append_history (nr_history_lines, histfile); - } -#endif -} - -static void -add_history_line (const char *line) -{ -#ifdef HAVE_LIBREADLINE - add_history (line); - nr_history_lines++; -#endif -} - -static int -compare (const void *vp1, const void *vp2) -{ - char * const *p1 = (char * const *) vp1; - char * const *p2 = (char * const *) vp2; - return strcasecmp (*p1, *p2); -} - -static void -sort_strings (char **strings, int len) -{ - qsort (strings, len, sizeof (char *), compare); -} - -static int -get_xdigit (char c) -{ - switch (c) { - case '0'...'9': return c - '0'; - case 'a'...'f': return c - 'a' + 10; - case 'A'...'F': return c - 'A' + 10; - default: return -1; - } -} - -static int -dispatch (char *cmd, char *args) -{ - if (STRCASEEQ (cmd, "help")) - return cmd_help (args); - else if (STRCASEEQ (cmd, "load")) - return cmd_load (args); - else if (STRCASEEQ (cmd, "exit") || - STRCASEEQ (cmd, "q") || - STRCASEEQ (cmd, "quit")) { - quit = 1; - return 0; - } - - /* If no hive file is loaded (!h) then only the small selection of - * commands above will work. - */ - if (!h) { - fprintf (stderr, _("hivexsh: you must load a hive file first using 'load hivefile'\n")); - return -1; - } - - if (STRCASEEQ (cmd, "add")) - return cmd_add (args); - else if (STRCASEEQ (cmd, "cd")) - return cmd_cd (args); - else if (STRCASEEQ (cmd, "close") || STRCASEEQ (cmd, "unload")) - return cmd_close (args); - else if (STRCASEEQ (cmd, "commit")) - return cmd_commit (args); - else if (STRCASEEQ (cmd, "del")) - return cmd_del (args); - else if (STRCASEEQ (cmd, "ls")) - return cmd_ls (args); - else if (STRCASEEQ (cmd, "lsval")) - return cmd_lsval (args); - else if (STRCASEEQ (cmd, "setval")) - return cmd_setval (args); - else { - fprintf (stderr, _("hivexsh: unknown command '%s', use 'help' for help summary\n"), - cmd); - return -1; - } -} - -static int -cmd_load (char *hivefile) -{ - if (STREQ (hivefile, "")) { - fprintf (stderr, _("hivexsh: load: no hive file name given to load\n")); - return -1; - } - - if (h) hivex_close (h); - h = NULL; - - free (loaded); - loaded = NULL; - - cwd = 0; - - h = hivex_open (hivefile, open_flags); - if (h == NULL) { - fprintf (stderr, - _( -"hivexsh: failed to open hive file: %s: %m\n" -"\n" -"If you think this file is a valid Windows binary hive file (_not_\n" -"a regedit *.reg file) then please run this command again using the\n" -"hivexsh option '-d' and attach the complete output _and_ the hive file\n" -"which fails into a bug report at https://bugzilla.redhat.com/\n" -"\n"), - hivefile); - return -1; - } - - /* Get the basename of the file for the prompt. */ - char *p = strrchr (hivefile, '/'); - if (p) - loaded = strdup (p+1); - else - loaded = strdup (hivefile); - if (!loaded) { - perror ("strdup"); - exit (EXIT_FAILURE); - } - - cwd = hivex_root (h); - - set_prompt_string (); - - return 0; -} - -static int -cmd_close (char *args) -{ - if (STRNEQ (args, "")) { - fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"), - "close"); - return -1; - } - - if (h) hivex_close (h); - h = NULL; - - free (loaded); - loaded = NULL; - - cwd = 0; - - set_prompt_string (); - - return 0; -} - -static int -cmd_commit (char *path) -{ - if (STREQ (path, "")) - path = NULL; - - if (hivex_commit (h, path, 0) == -1) { - perror ("hivexsh: commit"); - return -1; - } - - return 0; -} - -static int -cmd_cd (char *path) -{ - if (STREQ (path, "")) { - print_node_path (cwd, stdout); - fputc ('\n', stdout); - return 0; - } - - if (path[0] == '\\' && path[1] == '\\') { - fprintf (stderr, _("%s: %s: \\ characters in path are doubled - are you escaping the path parameter correctly?\n"), "hivexsh", path); - return -1; - } - - hive_node_h new_cwd = cwd; - hive_node_h root = hivex_root (h); - - if (path[0] == '\\') { - new_cwd = root; - path++; - } - - while (path[0]) { - size_t len = strcspn (path, "\\"); - if (len == 0) { - path++; - continue; - } - - char *elem = path; - path = path[len] == '\0' ? &path[len] : &path[len+1]; - elem[len] = '\0'; - - if (len == 1 && STREQ (elem, ".")) - continue; - - if (len == 2 && STREQ (elem, "..")) { - if (new_cwd != root) - new_cwd = hivex_node_parent (h, new_cwd); - continue; - } - - errno = 0; - new_cwd = hivex_node_get_child (h, new_cwd, elem); - if (new_cwd == 0) { - if (errno) - perror ("hivexsh: cd"); - else - fprintf (stderr, _("hivexsh: cd: subkey '%s' not found\n"), - elem); - return -1; - } - } - - if (new_cwd != cwd) { - cwd = new_cwd; - set_prompt_string (); - } - - return 0; -} - -static int -cmd_help (char *args) -{ - printf (_( -"Navigate through the hive's keys using the 'cd' command, as if it\n" -"contained a filesystem, and use 'ls' to list the subkeys of the\n" -"current key. Full documentation is in the hivexsh(1) manual page.\n")); - - return 0; -} - -static int -cmd_ls (char *args) -{ - if (STRNEQ (args, "")) { - fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"), - "ls"); - return -1; - } - - /* Get the subkeys. */ - hive_node_h *children = hivex_node_children (h, cwd); - if (children == NULL) { - perror ("ls"); - return -1; - } - - /* Get names for each subkey. */ - size_t len; - for (len = 0; children[len] != 0; ++len) - ; - - char **names = calloc (len, sizeof (char *)); - if (names == NULL) { - perror ("malloc"); - exit (EXIT_FAILURE); - } - - int ret = -1; - size_t i; - for (i = 0; i < len; ++i) { - names[i] = hivex_node_name (h, children[i]); - if (names[i] == NULL) { - perror ("hivex_node_name"); - goto error; - } - } - - /* Sort the names. */ - sort_strings (names, len); - - for (i = 0; i < len; ++i) - printf ("%s\n", names[i]); - - ret = 0; - error: - free (children); - for (i = 0; i < len; ++i) - free (names[i]); - free (names); - return ret; -} - -static int -cmd_lsval (char *key) -{ - if (STRNEQ (key, "")) { - hive_value_h value; - - errno = 0; - if (STREQ (key, "@")) /* default key written as "@" */ - value = hivex_node_get_value (h, cwd, ""); - else - value = hivex_node_get_value (h, cwd, key); - - if (value == 0) { - if (errno) - goto error; - /* else key not found */ - fprintf (stderr, _("%s: %s: key not found\n"), "hivexsh", key); - return -1; - } - - /* Print the value. */ - hive_type t; - size_t len; - if (hivex_value_type (h, value, &t, &len) == -1) - goto error; - - switch (t) { - case hive_t_string: - case hive_t_expand_string: - case hive_t_link: { - char *str = hivex_value_string (h, value); - if (!str) - goto error; - - puts (str); /* note: this adds a single \n character */ - free (str); - break; - } - - case hive_t_dword: - case hive_t_dword_be: { - int32_t j = hivex_value_dword (h, value); - printf ("%" PRIi32 "\n", j); - break; - } - - case hive_t_qword: { - int64_t j = hivex_value_qword (h, value); - printf ("%" PRIi64 "\n", j); - break; - } - - case hive_t_multiple_strings: { - char **strs = hivex_value_multiple_strings (h, value); - if (!strs) - goto error; - size_t j; - for (j = 0; strs[j] != NULL; ++j) { - puts (strs[j]); - free (strs[j]); - } - free (strs); - break; - } - - case hive_t_none: - case hive_t_binary: - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - default: { - char *data = hivex_value_value (h, value, &t, &len); - if (!data) - goto error; - - if (fwrite (data, 1, len, stdout) != len) - goto error; - - free (data); - break; - } - } /* switch */ - } else { - /* No key specified, so print all keys in this node. We do this - * in a format which looks like the output of regedit, although - * this isn't a particularly useful format. - */ - hive_value_h *values; - - values = hivex_node_values (h, cwd); - if (values == NULL) - goto error; - - size_t i; - for (i = 0; values[i] != 0; ++i) { - char *key = hivex_value_key (h, values[i]); - if (!key) goto error; - - if (*key) { - putchar ('"'); - size_t j; - for (j = 0; key[j] != 0; ++j) { - if (key[j] == '"' || key[j] == '\\') - putchar ('\\'); - putchar (key[j]); - } - putchar ('"'); - } else - printf ("\"@\""); /* default key in regedit files */ - putchar ('='); - free (key); - - hive_type t; - size_t len; - if (hivex_value_type (h, values[i], &t, &len) == -1) - goto error; - - switch (t) { - case hive_t_string: - case hive_t_expand_string: - case hive_t_link: { - char *str = hivex_value_string (h, values[i]); - if (!str) - goto error; - - if (t != hive_t_string) - printf ("str(%d):", t); - putchar ('"'); - size_t j; - for (j = 0; str[j] != 0; ++j) { - if (str[j] == '"' || str[j] == '\\') - putchar ('\\'); - putchar (str[j]); - } - putchar ('"'); - free (str); - break; - } - - case hive_t_dword: - case hive_t_dword_be: { - int32_t j = hivex_value_dword (h, values[i]); - printf ("dword:%08" PRIx32, j); - break; - } - - case hive_t_qword: /* sic */ - case hive_t_none: - case hive_t_binary: - case hive_t_multiple_strings: - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - default: { - unsigned char *data = - (unsigned char *) hivex_value_value (h, values[i], &t, &len); - if (!data) - goto error; - - printf ("hex(%d):", t); - size_t j; - for (j = 0; j < len; ++j) { - if (j > 0) - putchar (','); - printf ("%02x", data[j]); - } - break; - } - } /* switch */ - - putchar ('\n'); - } /* for */ - - free (values); - } - - return 0; - - error: - perror ("hivexsh: lsval"); - return -1; -} - -static int -cmd_setval (char *nrvals_str) -{ - strtol_error xerr; - - /* Parse number of values. */ - long nrvals; - xerr = xstrtol (nrvals_str, NULL, 0, &nrvals, ""); - if (xerr != LONGINT_OK) { - fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"), - "setval", "nrvals", "xstrtol", xerr); - return -1; - } - if (nrvals < 0 || nrvals > HIVEX_MAX_VALUES) { - fprintf (stderr, _("%s: %s: integer out of range\n"), - "setval", "nrvals"); - return -1; - } - - struct hive_set_value *values = - calloc (nrvals, sizeof (struct hive_set_value)); - if (values == NULL) { - perror ("calloc"); - exit (EXIT_FAILURE); - } - - int ret = -1; - - /* Read nrvals * 2 lines of input, nrvals * (key, value) pairs, as - * explained in the man page. - */ - int i, j; - for (i = 0; i < nrvals; ++i) { - /* Read key. */ - char *buf = rl_gets (" key> "); - if (!buf) { - fprintf (stderr, _("hivexsh: setval: unexpected end of input\n")); - quit = 1; - goto error; - } - - /* Note that buf will be overwritten by the next call to rl_gets. */ - if (STREQ (buf, "@")) - values[i].key = strdup (""); - else - values[i].key = strdup (buf); - if (values[i].key == NULL) { - perror ("strdup"); - exit (EXIT_FAILURE); - } - - /* Read value. */ - buf = rl_gets ("value> "); - if (!buf) { - fprintf (stderr, _("hivexsh: setval: unexpected end of input\n")); - quit = 1; - goto error; - } - - if (STREQ (buf, "none")) { - values[i].t = hive_t_none; - values[i].len = 0; - } - else if (STRPREFIX (buf, "string:")) { - buf += 7; - values[i].t = hive_t_string; - int nr_chars = strlen (buf); - values[i].len = 2 * (nr_chars + 1); - values[i].value = malloc (values[i].len); - if (!values[i].value) { - perror ("malloc"); - exit (EXIT_FAILURE); - } - for (j = 0; j <= /* sic */ nr_chars; ++j) { - if (buf[j] & 0x80) { - fprintf (stderr, _("hivexsh: string(utf16le): only 7 bit ASCII strings are supported for input\n")); - goto error; - } - values[i].value[2*j] = buf[j]; - values[i].value[2*j+1] = '\0'; - } - } - else if (STRPREFIX (buf, "expandstring:")) { - buf += 13; - values[i].t = hive_t_expand_string; - int nr_chars = strlen (buf); - values[i].len = 2 * (nr_chars + 1); - values[i].value = malloc (values[i].len); - if (!values[i].value) { - perror ("malloc"); - exit (EXIT_FAILURE); - } - for (j = 0; j <= /* sic */ nr_chars; ++j) { - if (buf[j] & 0x80) { - fprintf (stderr, _("hivexsh: string(utf16le): only 7 bit ASCII strings are supported for input\n")); - goto error; - } - values[i].value[2*j] = buf[j]; - values[i].value[2*j+1] = '\0'; - } - } - else if (STRPREFIX (buf, "dword:")) { - buf += 6; - values[i].t = hive_t_dword; - values[i].len = 4; - values[i].value = malloc (4); - if (!values[i].value) { - perror ("malloc"); - exit (EXIT_FAILURE); - } - long n; - xerr = xstrtol (buf, NULL, 0, &n, ""); - if (xerr != LONGINT_OK) { - fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"), - "setval", "dword", "xstrtol", xerr); - goto error; - } - if (n < 0 || n > UINT32_MAX) { - fprintf (stderr, _("%s: %s: integer out of range\n"), - "setval", "dword"); - goto error; - } - uint32_t u32 = htole32 (n); - memcpy (values[i].value, &u32, 4); - } - else if (STRPREFIX (buf, "qword:")) { - buf += 6; - values[i].t = hive_t_qword; - values[i].len = 8; - values[i].value = malloc (8); - if (!values[i].value) { - perror ("malloc"); - exit (EXIT_FAILURE); - } - long long n; - xerr = xstrtoll (buf, NULL, 0, &n, ""); - if (xerr != LONGINT_OK) { - fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"), - "setval", "dword", "xstrtoll", xerr); - goto error; - } -#if 0 - if (n < 0 || n > UINT64_MAX) { - fprintf (stderr, _("%s: %s: integer out of range\n"), - "setval", "dword"); - goto error; - } -#endif - uint64_t u64 = htole64 (n); - memcpy (values[i].value, &u64, 4); - } - else if (STRPREFIX (buf, "hex:")) { - /* Read the type. */ - buf += 4; - size_t len = strcspn (buf, ":"); - char *nextbuf; - if (buf[len] == '\0') /* "hex:t" */ - nextbuf = &buf[len]; - else { /* "hex:t:..." */ - buf[len] = '\0'; - nextbuf = &buf[len+1]; - } - - long t; - xerr = xstrtol (buf, NULL, 0, &t, ""); - if (xerr != LONGINT_OK) { - fprintf (stderr, _("%s: %s: invalid integer parameter (%s returned %d)\n"), - "setval", "hex", "xstrtol", xerr); - goto error; - } - if (t < 0 || t > UINT32_MAX) { - fprintf (stderr, _("%s: %s: integer out of range\n"), - "setval", "hex"); - goto error; - } - values[i].t = t; - - /* Read the hex data. */ - buf = nextbuf; - - /* The allocation length is an overestimate, but it doesn't matter. */ - values[i].value = malloc (1 + strlen (buf) / 2); - if (!values[i].value) { - perror ("malloc"); - exit (EXIT_FAILURE); - } - values[i].len = 0; - - while (*buf) { - int c = 0; - - for (j = 0; *buf && j < 2; buf++) { - if (c_isxdigit (*buf)) { /* NB: ignore non-hex digits. */ - c <<= 4; - c |= get_xdigit (*buf); - j++; - } - } - - if (j == 2) values[i].value[values[i].len++] = c; - else if (j == 1) { - fprintf (stderr, _("hivexsh: setval: trailing garbage after hex string\n")); - goto error; - } - } - } - else { - fprintf (stderr, - _("hivexsh: setval: cannot parse value string, please refer to the man page hivexsh(1) for help: %s\n"), - buf); - goto error; - } - } - - ret = hivex_node_set_values (h, cwd, nrvals, values, 0); - - error: - /* Free values array. */ - for (i = 0; i < nrvals; ++i) { - free (values[i].key); - free (values[i].value); - } - free (values); - - return ret; -} - -static int -cmd_del (char *args) -{ - if (STRNEQ (args, "")) { - fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"), - "del"); - return -1; - } - - if (cwd == hivex_root (h)) { - fprintf (stderr, _("hivexsh: del: the root node cannot be deleted\n")); - return -1; - } - - hive_node_h new_cwd = hivex_node_parent (h, cwd); - - if (hivex_node_delete_child (h, cwd) == -1) { - perror ("hivexsh: del"); - return -1; - } - - cwd = new_cwd; - set_prompt_string (); - return 0; -} - -static int -cmd_add (char *name) -{ - hive_node_h node = hivex_node_add_child (h, cwd, name); - if (node == 0) { - perror ("hivexsh: add"); - return -1; - } - return 0; -} diff --git a/hivex/hivexsh.pod b/hivex/hivexsh.pod deleted file mode 100644 index a31d9e0b..00000000 --- a/hivex/hivexsh.pod +++ /dev/null @@ -1,287 +0,0 @@ -=encoding utf8 - -=head1 NAME - -hivexsh - Windows Registry hive shell - -=head1 SYNOPSIS - - hivexsh [-options] [hivefile] - -=head1 DESCRIPTION - -This program provides a simple shell for navigating Windows Registry -'hive' files. It uses the hivex library for access to these binary -files. - -Firstly you will need to provide a hive file from a Windows operating -system. The hive files are usually located in -C<C:\Windows\System32\Config> and have names like C<software>, -C<system> etc (without any file extension). For more information -about hive files, read L<hivex(3)>. For information about downloading -files from virtual machines, read L<virt-cat(1)> and L<guestfish(1)>. - -You can provide the name of the hive file to examine on the command -line. For example: - - hivexsh software - -Or you can start C<hivexsh> without any arguments, and immediately use -the C<load> command to load a hive: - - $ hivexsh - - Welcome to hivexsh, the hivex interactive shell for examining - Windows Registry binary hive files. - - Type: 'help' for help with commands - 'quit' to quit the shell - - > load software - software\> - -Navigate through the hive's keys using the C<cd> command, as if it -contained a filesystem, and use C<ls> to list the subkeys of the -current key. Other commands are listed below. - -=head1 OPTIONS - -=over 4 - -=item B<-d> - -Enable lots of debug messages. If you find a Registry file that this -program cannot parse, please enable this option and post the complete -output I<and> the Registry hive file in your bug report. - -=item B<-f> filename - -Read commands from C<filename> instead of stdin. To write a hivexsh -script, use: - - #!/usr/bin/hivexsh -f - -=item B<-w> - -If this option is given, then writes are allowed to the hive -(see L</commit> command below, and the discussion of -modifying hives in L<hivex(3)/WRITING TO HIVE FILES>). - -B<Important Note:> Even if you specify this option, nothing is written -to a hive unless you call the L</commit> command. If you exit the -shell without committing, all changes will be discarded. - -If this option is not given, then write commands are disabled. - -=back - -=head1 COMMANDS - -=over 4 - -=item B<add> name - -Add a subkey named C<name> below the current node. The name may -contain spaces and punctuation characters, and does not need to be -quoted. - -The new key will have no subkeys and no values (see C<setval>). - -There must be no existing subkey called C<name>, or this command will -fail. To replace an existing subkey, delete it first like this: - - cd name - del - -=item B<cd> path - -Change to the subkey C<path>. Use Windows-style backslashes to -separate path elements, and start with a backslash in order to start -from the root of the hive. For example: - - cd \Classes\* - -moves from the root node, to the C<Classes> node, to the C<*> node. -If you were already at the root node, you could do this instead: - - cd Classes\* - -or even: - - cd Classes - cd * - -Path elements (node names) are matched case insensitively, and -characters like space, C<*>, and C<?> have I<no> special significance. - -C<cd ..> may be used to go to the parent directory. - -C<cd> without any arguments prints the current path. - -Be careful with C<cd \> since the readline library has an undocumented -behaviour where it will think the final backslash is a continuation -(it reads the next line of input and appends it). Put a single space -after the backslash. - -=item B<close> | B<unload> - -Close the currently loaded hive. - -If you modified the hive, all uncommitted writes are lost when you -call this command (or if the shell exits). You have to call C<commit> -to write changes. - -=item B<commit> [newfile] - -Commit changes to the hive. If the optional C<newfile> parameter is -supplied, then the hive is written to that file, else the original -file is overwritten. - -Note that you have to specify the C<-w> flag, otherwise no writes are -allowed. - -=item B<del> - -Delete the current node and everything beneath it. The current -directory is moved up one level (as if you did C<cd ..>) after -this command. - -You cannot delete the root node. - -=item B<exit> | B<quit> - -Exit the shell. - -=item B<load> hivefile - -Load the binary hive named C<hivefile>. The currently loaded hive, if -any, is closed. The current directory is changed back to the root -node. - -=item B<ls> - -List the subkeys of the current hive Registry key. Note this command -does not take any arguments. - -=item B<lsval> [key] - -List the (key, value) pairs of the current hive Registry key. If no -argument is given then all pairs are displayed. If C<key> is given, -then the value of the named key is displayed. If C<@> is given, then -the value of the default key is displayed. - -=item B<setval> nrvals - -This command replaces all (key, value) pairs at the current node with -the values in subsequent input. C<nrvals> is the number of values -(ie. (key, value) pairs), and any existing values at this node are -deleted. So C<setval 0> just deletes any values at the current node. - -The command reads 2 * nrvals lines of input, with each pair of -lines of input corresponding to a key and a value to add. - -For example, the following setval command replaces whatever is at the -current node with two (key, value) pairs. The default key is set to -the UTF16-LE-encoded string "abcd". The other value is named -"ANumber" and is a little-endian DWORD 0x12345678. - - setval 2 - @ - string:abcd - ANumber - dword:12345678 - -The first line of each pair is the key (the special key C<@> means -the default key, but you can also use a blank line). - -The second line of each pair is the value, which has a special format -C<type:value> with possible types summarized in the table below: - - none No data is stored, and the type is set to 0. - - string:abc "abc" is stored as a UTF16-LE-encoded - string (type 1). Note that only 7 bit - ASCII strings are supported as input. - - expandstring:... Same as string but with type 2. - - dword:0x01234567 A DWORD (type 4) with the hex value - 0x01234567. You can also use decimal - or octal numbers here. - - qword:0x0123456789abcdef - A QWORD (type 11) with the hex value - 0x0123456789abcdef. You can also use - decimal or octal numbers here. - - hex:<type>:<hexbytes> - hex:1:41,00,42,00,43,00,44,00,00,00 - This is the generic way to enter any - value. <type> is the integer value type. - <hexbytes> is a list of pairs of hex - digits which are treated as bytes. - (Any non-hex-digits here are ignored, - so you can separate bytes with commas - or spaces if you want). - -=back - -=head1 EXAMPLE - - $ guestfish --ro -i Windows7 - ><fs> download win:c:\windows\system32\config\software software - ><fs> quit - - $ hivexsh software - - Welcome to hivexsh, the hivex interactive shell for examining - Windows Registry binary hive files. - - Type: 'help' for help with commands - 'quit' to quit the shell - - software\> ls - ATI Technologies - Classes - Clients - Intel - Microsoft - ODBC - Policies - RegisteredApplications - Sonic - Wow6432Node - software\> quit - -=head1 SEE ALSO - -L<hivex(3)>, -L<hivexget(1)>, -L<hivexml(1)>, -L<virt-win-reg(1)>, -L<guestfs(3)>, -L<http://libguestfs.org/>, -L<virt-cat(1)>, -L<virt-edit(1)>. - -=head1 AUTHORS - -Richard W.M. Jones (C<rjones at redhat dot com>) - -=head1 COPYRIGHT - -Copyright (C) 2009-2010 Red Hat Inc. - -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. diff --git a/hivex/t/Makefile.am b/hivex/t/Makefile.am deleted file mode 100644 index 217d7caa..00000000 --- a/hivex/t/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -# libguestfs -# Copyright (C) 2009 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -EXTRA_DIST = minimal diff --git a/hivex/t/README b/hivex/t/README deleted file mode 100644 index b7e297ac..00000000 --- a/hivex/t/README +++ /dev/null @@ -1,9 +0,0 @@ -This directory contains tests for the hivex library. - -'minimal' is a valid registry containing a single root nk (with -associated sk) which was created by chopping out everything possible -from a Windows 2003 software hive and then doing lots of hand edits on -the result. There is no "source" for it as such, it is just a -hand-crafted binary blob. - -- Richard W.M. Jones 2010-01-23. diff --git a/hivex/t/minimal b/hivex/t/minimal Binary files differdeleted file mode 100755 index 3f4ee58c..00000000 --- a/hivex/t/minimal +++ /dev/null diff --git a/hivex/tools/Makefile.am b/hivex/tools/Makefile.am deleted file mode 100644 index bd8e9861..00000000 --- a/hivex/tools/Makefile.am +++ /dev/null @@ -1,56 +0,0 @@ -# libguestfs -# Copyright (C) 2009 Red Hat Inc. -# -# 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -# OCaml Windows Registry visualizer. This was used while reverse -# engineering the hive format, and is not normally compiled. If you -# do with to compile it, you'll need ocaml-bitstring-devel and -# ocaml-extlib-devel. Also you'll need a collection of hive files -# from Windows machines to experiment with. -# -# We use '-w y' (disable unused variable warnings) because these -# warnings aren't very reliable with heavily preprocessed code like -# that produced by bitstring. - -EXTRA_DIST = \ - visualizer.ml \ - visualizer_utils.ml \ - visualizer_NT_time.ml \ - clearheaderfields.ml \ - fillemptyhbins.ml \ - truncatefile.ml \ - counter.mli \ - counter.ml - -visualizer.opt: counter.mli counter.ml visualizer_utils.ml visualizer_NT_time.ml visualizer.ml - ocamlfind ocamlopt -w y \ - -package bitstring,bitstring.syntax,extlib \ - -syntax camlp4 -linkpkg $^ -o $@ - -fillemptyhbins.opt: fillemptyhbins.ml - ocamlfind ocamlopt -w y \ - -package bitstring,bitstring.syntax,extlib \ - -syntax camlp4 -linkpkg $^ -o $@ - -clearheaderfields.opt: visualizer_utils.ml clearheaderfields.ml - ocamlfind ocamlopt -w y \ - -package bitstring,bitstring.syntax,extlib \ - -syntax camlp4 -linkpkg $^ -o $@ - -truncatefile.opt: visualizer_utils.ml truncatefile.ml - ocamlfind ocamlopt -w y \ - -package bitstring,bitstring.syntax,extlib \ - -syntax camlp4 -linkpkg $^ -o $@ diff --git a/hivex/tools/clearheaderfields.ml b/hivex/tools/clearheaderfields.ml deleted file mode 100644 index d0555532..00000000 --- a/hivex/tools/clearheaderfields.ml +++ /dev/null @@ -1,112 +0,0 @@ -(* Windows Registry reverse-engineering tool. - * Copyright (C) 2010 Red Hat Inc. - * - * 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. - *) - -open Bitstring -open ExtString -open Printf -open Visualizer_utils - -let () = - if Array.length Sys.argv <> 2 then ( - eprintf "Error: missing argument. -Usage: %s hivefile -" Sys.executable_name; - exit 1 - ) - -let filename = Sys.argv.(1) - -(* Load the file. *) -let bits = bitstring_of_file filename - -(* Split into header + data at the 4KB boundary. *) -let header, data = takebits (4096 * 8) bits, dropbits (4096 * 8) bits - -(* Read the header fields. *) -let seq, last_modified, major, minor, unknown1, unknown2, - root_key, end_pages, unknown3, fname = - bitmatch header with - | { "regf" : 4*8 : string; - seq1 : 4*8 : littleendian; - seq2 : 4*8 : littleendian; - last_modified : 64 : bitstring; - major : 4*8 : littleendian; - minor : 4*8 : littleendian; - unknown1 : 4*8 : littleendian; - unknown2 : 4*8 : littleendian; - root_key : 4*8 : littleendian; - end_pages : 4*8 : littleendian; - unknown3 : 4*8 : littleendian; - fname : 64*8 : string; - unknownguid1 : 16*8 : bitstring; - unknownguid2 : 16*8 : bitstring; - unknown4 : 4*8 : littleendian; - unknownguid3 : 16*8 : bitstring; - unknown5 : 4*8 : string; - unknown6 : 340*8 : bitstring; - csum : 4*8 - : littleendian, save_offset_to (crc_offset), - check (assert (crc_offset = 0x1fc * 8); true); - unknown7 : (0x1000-0x200)*8 : bitstring } -> - seq1, last_modified, major, minor, unknown1, unknown2, - root_key, end_pages, unknown3, fname - | {_} -> assert false - -(* Create a new header, but with unknown fields cleared. Do it in - * two parts, first creating everything up to the checksum, then - * calculating the checksum and appending checksum and the final - * field. - *) -let header = - let zeroguid = zeroes_bitstring (16*8) in - let before_csum = - BITSTRING { - "regf" : 4*8 : string; - seq : 4*8 : littleendian; - seq : 4*8 : littleendian; - last_modified : 64 : bitstring; - major : 4*8 : littleendian; - minor : 4*8 : littleendian; - unknown1 : 4*8 : littleendian; - unknown2 : 4*8 : littleendian; - root_key : 4*8 : littleendian; - end_pages : 4*8 : littleendian; - unknown3 : 4*8 : littleendian; - fname : 64*8 : string; - zeroguid : 16*8 : bitstring; - zeroguid : 16*8 : bitstring; - 0_l : 4*8 : littleendian; - zeroguid : 16*8 : bitstring; - 0_l : 4*8 : littleendian; - zeroes_bitstring (340*8) : 340*8 : bitstring - } in - assert (bitstring_length before_csum = 0x1fc * 8); - let csum = bitstring_fold_left_int32_le Int32.logxor 0_l before_csum in - let csum_and_after = - BITSTRING { - csum : 4*8 : littleendian; - zeroes_bitstring ((0x1000-0x200)*8) : (0x1000-0x200)*8 : bitstring - } in - let new_header = concat [before_csum; csum_and_after] in - assert (bitstring_length header = bitstring_length new_header); - new_header - -(* Write it. *) -let () = - let file = concat [header; data] in - bitstring_to_file file filename diff --git a/hivex/tools/counter.ml b/hivex/tools/counter.ml deleted file mode 100644 index 2e44c653..00000000 --- a/hivex/tools/counter.ml +++ /dev/null @@ -1,86 +0,0 @@ -(* Basic counting module. - - Copyright (C) 2006 Merjis Ltd. - - 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 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 -*) - -type 'a t = ('a, int ref) Hashtbl.t - -let create () = - Hashtbl.create 13 - -let get_ref counter thing = - try - Hashtbl.find counter thing - with - Not_found -> - let r = ref 0 in - Hashtbl.add counter thing r; - r - -let incr counter thing = - let r = get_ref counter thing in - incr r - -let decr counter thing = - let r = get_ref counter thing in - decr r - -let add counter thing n = - let r = get_ref counter thing in - r := !r + n - -let sub counter thing n = - let r = get_ref counter thing in - r := !r - n - -let set counter thing n = - let r = get_ref counter thing in - r := n - -(* Don't use get_ref, to avoid unnecessarily creating 'ref 0's. *) -let get counter thing = - try - !(Hashtbl.find counter thing) - with - Not_found -> 0 - -(* This is a common pair of operations, worth optimising. *) -let incr_get counter thing = - let r = get_ref counter thing in - Pervasives.incr r; - !r - -let zero = Hashtbl.remove - -let read counter = - let counts = - Hashtbl.fold ( - fun thing r xs -> - let r = !r in - if r <> 0 then (r, thing) :: xs - else xs - ) counter [] in - List.sort (fun (a, _) (b, _) -> compare (b : int) (a : int)) counts - -let length = Hashtbl.length - -let total counter = - let total = ref 0 in - Hashtbl.iter (fun _ r -> total := !total + !r) counter; - !total - -let clear = Hashtbl.clear diff --git a/hivex/tools/counter.mli b/hivex/tools/counter.mli deleted file mode 100644 index 87610b59..00000000 --- a/hivex/tools/counter.mli +++ /dev/null @@ -1,69 +0,0 @@ -(** Basic counting module. - - Copyright (C) 2006 Merjis Ltd. - - 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 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 -*) - -type 'a t -(** Count items of type ['a]. *) - -val create : unit -> 'a t -(** Create a new counter. *) - -val incr : 'a t -> 'a -> unit -(** [incr counter thing] adds one to the count of [thing]s in [counter]. *) - -val decr : 'a t -> 'a -> unit -(** [decr counter thing] subtracts one to the count of [thing]s in [counter]. *) - -val add : 'a t -> 'a -> int -> unit -(** [add counter thing n] adds [n] to the count of [thing]s in [counter]. *) - -val sub : 'a t -> 'a -> int -> unit -(** [sub counter thing n] subtracts [n] to the count of [thing]s in [counter]. *) - -val set : 'a t -> 'a -> int -> unit -(** [set counter thing n] sets the count of [thing]s to [n]. *) - -val get : 'a t -> 'a -> int -(** [get counter thing] returns the count of [thing]s. (Returns 0 for - * [thing]s which have not been added. - *) - -val incr_get : 'a t -> 'a -> int -(** Faster form of {!Counter.incr} followed by {!Counter.get}. *) - -val zero : 'a t -> 'a -> unit -(** [zero counter thing] sets the count of [thing]s to 0. - * See also {!Counter.clear}. - *) - -val read : 'a t -> (int * 'a) list -(** [read counter] reads the frequency of each thing. They are sorted - * with the thing appearing most frequently first. Only things occurring - * non-zero times are returned. - *) - -val length : 'a t -> int -(** Return the number of distinct things. See also {!Counter.total} *) - -val total : 'a t -> int -(** Return the number of things counted (the total number of counts). - * See also {!Counter.length} - *) - -val clear : 'a t -> unit -(** [clear counter] zeroes all counts. *) diff --git a/hivex/tools/fillemptyhbins.ml b/hivex/tools/fillemptyhbins.ml deleted file mode 100644 index 14eae963..00000000 --- a/hivex/tools/fillemptyhbins.ml +++ /dev/null @@ -1,74 +0,0 @@ -(* Windows Registry reverse-engineering tool. - * Copyright (C) 2010 Red Hat Inc. - * - * 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. - *) - -open Bitstring -open ExtString -open Printf - -let () = - if Array.length Sys.argv <> 3 then ( - eprintf "Error: missing argument. -Usage: %s hivefile startoffset -" Sys.executable_name; - exit 1 - ) - -let filename = Sys.argv.(1) -let offset = int_of_string Sys.argv.(2) - -(* Load the file. *) -let bits = bitstring_of_file filename - -(* Split into header + data at the 4KB boundary. *) -let header, data = takebits (4096 * 8) bits, dropbits (4096 * 8) bits - -(* Overwrite everything after @offset, so ... *) -let nrpages = (bitstring_length data / 8 - offset) / 4096 -let data = takebits (offset * 8) data - -(* Create the empty pages. They're not all the same because each - * page contains its own page_offset. - *) -let pages = - let noblock = - let seg_len = 4096 - 32 in - let zeroes = zeroes_bitstring ((seg_len - 4) * 8) in - BITSTRING { - Int32.of_int seg_len : 4*8 : littleendian; - zeroes : (seg_len - 4) * 8 : bitstring - } in - let zeroes = zeroes_bitstring (20*8) in - let rec loop page_offset i = - if i < nrpages then ( - let page = - BITSTRING { - "hbin" : 4*8 : string; - Int32.of_int page_offset : 4*8 : littleendian; - 4096_l : 4*8 : littleendian; (* page length *) - zeroes : 20*8 : bitstring; - noblock : (4096 - 32) * 8 : bitstring - } in - page :: loop (page_offset + 4096) (i+1) - ) else [] - in - loop offset 0 - -(* Write it. *) -let () = - let file = concat (header :: data :: pages) in - bitstring_to_file file filename diff --git a/hivex/tools/truncatefile.ml b/hivex/tools/truncatefile.ml deleted file mode 100644 index b519f7a5..00000000 --- a/hivex/tools/truncatefile.ml +++ /dev/null @@ -1,112 +0,0 @@ -(* Windows Registry reverse-engineering tool. - * Copyright (C) 2010 Red Hat Inc. - * - * 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. - *) - -open Bitstring -open ExtString -open Printf -open Visualizer_utils - -let () = - if Array.length Sys.argv <> 3 then ( - eprintf "Error: missing argument. -Usage: %s hivefile endpages -" Sys.executable_name; - exit 1 - ) - -let filename = Sys.argv.(1) -let new_end_pages = int_of_string Sys.argv.(2) - -(* Load the file. *) -let bits = bitstring_of_file filename - -(* Split into header + data at the 4KB boundary. *) -let header, data = takebits (4096 * 8) bits, dropbits (4096 * 8) bits - -(* Truncate the file data. *) -let data = takebits (new_end_pages * 8) data - -(* Read the header fields. *) -let seq, last_modified, major, minor, unknown1, unknown2, - root_key, end_pages, unknown3, fname = - bitmatch header with - | { "regf" : 4*8 : string; - seq1 : 4*8 : littleendian; - seq2 : 4*8 : littleendian; - last_modified : 64 : bitstring; - major : 4*8 : littleendian; - minor : 4*8 : littleendian; - unknown1 : 4*8 : littleendian; - unknown2 : 4*8 : littleendian; - root_key : 4*8 : littleendian; - end_pages : 4*8 : littleendian; - unknown3 : 4*8 : littleendian; - fname : 64*8 : string; - unknownguid1 : 16*8 : bitstring; - unknownguid2 : 16*8 : bitstring; - unknown4 : 4*8 : littleendian; - unknownguid3 : 16*8 : bitstring; - unknown5 : 4*8 : string; - unknown6 : 340*8 : bitstring; - csum : 4*8 - : littleendian, save_offset_to (crc_offset), - check (assert (crc_offset = 0x1fc * 8); true); - unknown7 : (0x1000-0x200)*8 : bitstring } -> - seq1, last_modified, major, minor, unknown1, unknown2, - root_key, end_pages, unknown3, fname - | {_} -> assert false - -(* Create a new header, with endpages updated. *) -let header = - let zeroguid = zeroes_bitstring (16*8) in - let before_csum = - BITSTRING { - "regf" : 4*8 : string; - seq : 4*8 : littleendian; - seq : 4*8 : littleendian; - last_modified : 64 : bitstring; - major : 4*8 : littleendian; - minor : 4*8 : littleendian; - unknown1 : 4*8 : littleendian; - unknown2 : 4*8 : littleendian; - root_key : 4*8 : littleendian; - Int32.of_int new_end_pages : 4*8 : littleendian; - unknown3 : 4*8 : littleendian; - fname : 64*8 : string; - zeroguid : 16*8 : bitstring; - zeroguid : 16*8 : bitstring; - 0_l : 4*8 : littleendian; - zeroguid : 16*8 : bitstring; - 0_l : 4*8 : littleendian; - zeroes_bitstring (340*8) : 340*8 : bitstring - } in - assert (bitstring_length before_csum = 0x1fc * 8); - let csum = bitstring_fold_left_int32_le Int32.logxor 0_l before_csum in - let csum_and_after = - BITSTRING { - csum : 4*8 : littleendian; - zeroes_bitstring ((0x1000-0x200)*8) : (0x1000-0x200)*8 : bitstring - } in - let new_header = concat [before_csum; csum_and_after] in - assert (bitstring_length header = bitstring_length new_header); - new_header - -(* Write it. *) -let () = - let file = concat [header; data] in - bitstring_to_file file filename diff --git a/hivex/tools/visualizer.ml b/hivex/tools/visualizer.ml deleted file mode 100644 index 5b7ac79d..00000000 --- a/hivex/tools/visualizer.ml +++ /dev/null @@ -1,984 +0,0 @@ -(* Windows Registry reverse-engineering tool. - * Copyright (C) 2010 Red Hat Inc. - * - * 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. - * - * For existing information on the registry format, please refer - * to the following documents. Note they are both incomplete - * and inaccurate in some respects. - * - * http://www.sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf - * http://pogostick.net/~pnh/ntpasswd/WinReg.txt - *) - -open Bitstring -open ExtString -open Printf -open Visualizer_utils -open Visualizer_NT_time - -let () = - if Array.length Sys.argv <> 2 then ( - eprintf "Error: missing argument. -Usage: %s hivefile > out -where - 'hivefile' is the input hive file from a Windows machine - 'out' is an output file where we will write all the keys, - values etc for extended debugging purposes. -Errors, inconsistencies and unexpected fields in the hive file -are written to stderr. -" Sys.executable_name; - exit 1 - ) - -let filename = Sys.argv.(1) -let basename = Filename.basename filename - -(* Load the file. *) -let bits = bitstring_of_file filename - -(* Split into header + data at the 4KB boundary. *) -let header, data = takebits (4096 * 8) bits, dropbits (4096 * 8) bits - -(* Define a persistent pattern which matches the header fields. By - * using persistent patterns, we can reuse them later in the - * program. - *) -let bitmatch header_fields = - { "regf" : 4*8 : string; - seq1 : 4*8 : littleendian; - seq2 : 4*8 : littleendian; - last_modified : 64 - : littleendian, bind (nt_to_time_t last_modified); - major : 4*8 : littleendian; - minor : 4*8 : littleendian; - - (* "Type". Contains 0. *) - unknown1 : 4*8 : littleendian; - - (* "Format". Contains 1. *) - unknown2 : 4*8 : littleendian; - - root_key : 4*8 - : littleendian, bind (get_offset root_key); - end_pages : 4*8 - : littleendian, bind (get_offset end_pages); - - (* "Cluster". Contains 1. *) - unknown3 : 4*8 : littleendian; - - filename : 64*8 : string; - - (* All three GUIDs here confirmed in Windows 7 registries. In - * Windows <= 2003 these GUID fields seem to contain junk. - * - * If you write zeroes to the GUID fields, load and unload in Win7 - * REGEDIT, then Windows 7 writes some random GUIDs. - * - * Also (on Win7) unknownguid1 == unknownguid2. unknownguid3 is - * different. - *) - unknownguid1 : 16*8 : bitstring; - unknownguid2 : 16*8 : bitstring; - - (* Wrote zero to unknown4, loaded and unloaded it in Win7 REGEDIT, - * and it still contained zero. In existing registries it seems to - * contain random junk. - *) - unknown4 : 4*8 : littleendian; - unknownguid3 : 16*8 : bitstring; - - (* If you write zero to unknown5, load and unload it in REGEDIT, - * Windows 7 puts the string "rmtm" here. Existing registries also - * seen containing this string. However on older Windows it can - * be all zeroes. - *) - unknown5 : 4*8 : string; - - (* This seems to contain junk from other parts of the registry. I - * wrote zeroes here, loaded and unloaded it in Win7 REGEDIT, and - * it still contained zeroes. - *) - unknown6 : 340*8 : bitstring; - csum : 4*8 - : littleendian, save_offset_to (crc_offset), - check (assert (crc_offset = 0x1fc * 8); true); - unknown7 : (0x1000-0x200)*8 : bitstring } - -let fprintf_header chan bits = - bitmatch bits with - | { :header_fields } -> - fprintf chan - "HD %6ld %6ld %s %ld.%ld %08lx %08lx %s %s %08lx %s %s %s %08lx %s %s %s %08lx %s\n" - seq1 seq2 (print_time last_modified) major minor - unknown1 unknown2 - (print_offset root_key) (print_offset end_pages) - unknown3 (print_utf16 filename) - (print_guid unknownguid1) (print_guid unknownguid2) - unknown4 (print_guid unknownguid3) unknown5 - (print_bitstring unknown6) - csum (print_bitstring unknown7) - -(* Parse the header and check it. *) -let root_key, end_pages = - bitmatch header with - | { :header_fields } -> - fprintf_header stdout header; - - if major <> 1_l then - eprintf "HD hive file major <> 1 (major.minor = %ld.%ld)\n" - major minor; - if seq1 <> seq2 then - eprintf "HD hive file sequence numbers should match (%ld <> %ld)\n" - seq1 seq2; - if unknown1 <> 0_l then - eprintf "HD unknown1 field <> 0 (%08lx)\n" unknown1; - if unknown2 <> 1_l then - eprintf "HD unknown2 field <> 1 (%08lx)\n" unknown2; - if unknown3 <> 1_l then - eprintf "HD unknown3 field <> 1 (%08lx)\n" unknown3; - if not (equals unknownguid1 unknownguid2) then - eprintf "HD unknownguid1 <> unknownguid2 (%s, %s)\n" - (print_guid unknownguid1) (print_guid unknownguid2); - (* We think this is junk. - if unknown4 <> 0_l then - eprintf "HD unknown4 field <> 0 (%08lx)\n" unknown4; - *) - if unknown5 <> "rmtm" && unknown5 <> "\000\000\000\000" then - eprintf "HD unknown5 field <> \"rmtm\" & <> zeroes (%s)\n" unknown5; - (* We think this is junk. - if not (is_zero_bitstring unknown6) then - eprintf "HD unknown6 area is not zero (%s)\n" - (print_bitstring unknown6); - *) - if not (is_zero_bitstring unknown7) then - eprintf "HD unknown7 area is not zero (%s)\n" - (print_bitstring unknown7); - - root_key, end_pages - | {_} -> - failwithf "%s: this doesn't look like a registry hive file\n" basename - -(* Define persistent patterns to match page and block fields. *) -let bitmatch page_fields = - { "hbin" : 4*8 : string; - page_offset : 4*8 - : littleendian, bind (get_offset page_offset); - page_size : 4*8 - : littleendian, check (Int32.rem page_size 4096_l = 0_l), - bind (Int32.to_int page_size); - - (* In the first hbin in the file these fields contain something. - * In subsequent hbins these fields are all zero. - * - * From existing hives (first hbin only): - * - * unknown1 unknown2 unknown5 - * 00 00 00 00 00 00 00 00 9C 77 3B 02 6A 7D CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 50 3A 15 07 B5 9B CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 57 86 90 D4 9A 58 CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 52 3F 90 9D CF 7C CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 E8 86 C1 17 BD 06 CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 4A 77 CE 7A CF 7C CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 E4 EA 23 FF 69 7D CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 50 13 BA 8D A2 9A CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 0E 07 93 13 BD 06 CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 9D 55 D0 B3 99 58 CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 46 AC FF 8B CF 7C CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 80 29 2D 02 6A 7D CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 90 8D 36 07 B5 9B CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 5C 9B 8B B8 6A 06 CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 85 9F BB 99 9A 58 CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 BE 3D 21 02 6A 7D CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 70 53 09 07 B5 9B CA 01 00 00 00 00 - * 00 00 00 00 00 00 00 00 5B 62 42 B6 9A 58 CA 01 00 00 00 00 - * 01 00 00 00 00 00 00 00 B2 46 9B 9E CF 7C CA 01 00 00 00 00 - * 01 00 00 00 00 00 00 00 CA 88 EE 1A BD 06 CA 01 00 00 00 00 - * - * From the above we worked out that fields 3 and 4 are an NT - * timestamp, which seems to be "last modified" (when REGEDIT - * unloads a hive it updates this timestamp even if nothing - * has been changed). - *) - unknown1 : 4*8 : littleendian; (* usually zero, occasionally 1 *) - unknown2 : 4*8 : littleendian; (* always zero? *) - last_modified : 64 - : littleendian, - bind (if page_offset = 0 then nt_to_time_t last_modified - else ( - assert (last_modified = 0_L); - 0. - ) - ); - (* The "B.D." document said this field contains the page size, but - * this is not true. This misinformation has been copied to the - * sentinelchicken documentation too. - *) - unknown5 : 4*8 : littleendian; (* always zero? *) - - (* Now the blocks in this page follow. *) - blocks : (page_size - 32) * 8 : bitstring; - - rest : -1 : bitstring } - -let fprintf_page chan bits = - bitmatch bits with - | { :page_fields } -> - fprintf chan "HB %s %08x %08lx %08lx %s %08lx\n" - (print_offset page_offset) - page_size unknown1 unknown2 - (if page_offset = 0 then print_time last_modified - else string_of_float last_modified) unknown5 - -let bitmatch block_fields = - { seg_len : 4*8 - : littleendian, bind (Int32.to_int seg_len); - block_data : (abs seg_len - 4) * 8 : bitstring; - rest : -1 : bitstring } - -let fprintf_block chan block_offset bits = - bitmatch bits with - | { :block_fields } -> - fprintf chan "BL %s %s %d\n" - (print_offset block_offset) - (if seg_len < 0 then "used" else "free") - (if seg_len < 0 then -seg_len else seg_len) - -(* Iterate over the pages and blocks. In the process we will examine - * each page (hbin) header. Also we will build block_list which is a - * list of (block offset, length, used flag, data). - *) -let block_list = ref [] -let () = - let rec loop_over_pages data data_offset = - if data_offset < end_pages then ( - bitmatch data with - | { rest : -1 : bitstring } when bitstring_length rest = 0 -> () - - | { :page_fields } -> - fprintf_page stdout data; - - assert (page_offset = data_offset); - - if data_offset = 0 then ( (* first hbin only *) - if unknown1 <> 0_l then - eprintf "HB %s unknown1 field <> 0 (%08lx)\n" - (print_offset page_offset) unknown1; - if unknown2 <> 0_l then - eprintf "HB %s unknown2 field <> 0 (%08lx)\n" - (print_offset page_offset) unknown2; - if unknown5 <> 0_l then - eprintf "HB %s unknown5 field <> 0 (%08lx)\n" - (print_offset page_offset) unknown5 - ) else ( (* subsequent hbins *) - if unknown1 <> 0_l || unknown2 <> 0_l || unknown5 <> 0_l then - eprintf "HB %s unknown fields <> 0 (%08lx %08lx %08lx)\n" - (print_offset page_offset) - unknown1 unknown2 unknown5; - if last_modified <> 0. then - eprintf "HB %s last_modified <> 0. (%g)\n" - (print_offset page_offset) last_modified - ); - - (* Loop over the blocks in this page. *) - loop_over_blocks blocks (data_offset + 32); - - (* Loop over rest of the pages. *) - loop_over_pages rest (data_offset + page_size) - - | {_} -> - failwithf "%s: invalid hbin at offset %s\n" - basename (print_offset data_offset) - ) else ( - (* Reached the end of the official hbins in this file, BUT the - * file can be larger than this and might contain stuff. What - * does it contain after the hbins? We think just junk, but - * we're not sure. - *) - if not (is_zero_bitstring data) then ( - eprintf "Junk in file after end of pages:\n"; - let rec loop data data_offset = - bitmatch data with - | { rest : -1 : bitstring } when bitstring_length rest = 0 -> () - | { :page_fields } -> - eprintf "\tjunk hbin %s 0x%08x\n" - (print_offset data_offset) page_size; - loop rest (data_offset + page_size); - | { _ } -> - eprintf "\tother junk %s %s\n" - (print_offset data_offset) (print_bitstring data) - in - loop data data_offset - ) - ) - and loop_over_blocks blocks block_offset = - bitmatch blocks with - | { rest : -1 : bitstring } when bitstring_length rest = 0 -> () - - | { :block_fields } -> - assert (block_offset mod 8 = 0); - - fprintf_block stdout block_offset blocks; - - let used, seg_len = - if seg_len < 0 then true, -seg_len else false, seg_len in - - let block = block_offset, (seg_len, used, block_data) in - block_list := block :: !block_list; - - (* Loop over the rest of the blocks in this page. *) - loop_over_blocks rest (block_offset + seg_len) - - | {_} -> - failwithf "%s: invalid block near offset %s\n" - basename (print_offset block_offset) - in - loop_over_pages data 0 - -(* Turn the block_list into a map so we can quickly look up a block - * from its offset. - *) -let block_list = !block_list -let block_map = - List.fold_left ( - fun map (block_offset, block) -> IntMap.add block_offset block map - ) IntMap.empty block_list -let lookup fn offset = - try - let (_, used, _) as block = IntMap.find offset block_map in - if not used then - failwithf "%s: %s: lookup: free block %s referenced from hive tree" - basename fn (print_offset offset); - block - with Not_found -> - failwithf "%s: %s: lookup: unknown block %s referenced from hive tree" - basename fn (print_offset offset) - -(* Use this to mark blocks that we've visited. If the hive contains - * no unreferenced blocks, then by the end this should just contain - * free blocks. - *) -let mark_visited, is_not_visited, unvisited_blocks = - let v = ref block_map in - let mark_visited offset = v := IntMap.remove offset !v - and is_not_visited offset = IntMap.mem offset !v - and unvisited_blocks () = !v in - mark_visited, is_not_visited, unvisited_blocks - -(* Define persistent patterns to match nk-records, vk-records and - * sk-records, which are the record types that we especially want to - * analyze later. Other blocks types (eg. value lists, lf-records) - * have no "spare space" so everything is known about them and we don't - * store these. - *) -let bitmatch nk_fields = - { "nk" : 2*8 : string; - (* Flags stored in the file as a little endian word, hence the - * unusual ordering: - *) - virtmirrored : 1; - predefinedhandle : 1; keynameascii : 1; symlinkkey : 1; - cannotbedeleted : 1; isroot : 1; ismountpoint : 1; isvolatile : 1; - unknownflag8000 : 1; unknownflag4000 : 1; - unknownflag2000 : 1; unknownflag1000 : 1; - unknownflag0800 : 1; unknownflag0400 : 1; - virtualstore : 1; virttarget : 1; - timestamp : 64 : littleendian, bind (nt_to_time_t timestamp); - unknown1 : 4*8 : littleendian; - parent : 4*8 : littleendian, bind (get_offset parent); - nr_subkeys : 4*8 : littleendian, bind (Int32.to_int nr_subkeys); - nr_subkeys_vol : 4*8; - subkeys : 4*8 : littleendian, bind (get_offset subkeys); - subkeys_vol : 4*8; - nr_values : 4*8 : littleendian, bind (Int32.to_int nr_values); - vallist : 4*8 : littleendian, bind (get_offset vallist); - sk : 4*8 : littleendian, bind (get_offset sk); - classname : 4*8 : littleendian, bind (get_offset classname); - (* sentinelchicken.com says this is a single 32 bit field - * containing maximum number of bytes in a subkey name, however - * that does not seem to be correct. We think it is several - * fields, the first being the maximum number of bytes in the - * UTF16-LE encoded version of the subkey names, (since subkey - * names are usually ASCII, that would be max length of names * 2). - * This is a historical maximum, so it can be greater than the - * current maximum name field. - * - * The remaining fields are often non-zero, but the purpose is - * unknown. - * - * In the hives we examined the other fields had values as - * follows: - * userflags: 0, 2, 0xa, 0xe - * virtcontrolflags: 0, 1 - * debug: always 0 - *) - max_subkey_name_len : 2*8 : littleendian; - unknown2_userflags : 4; - unknown2_virtcontrolflags : 4; - unknown2_debug : 8; - - (* sentinelchicken.com says: maximum subkey CLASSNAME length, - * however that does not seem to be correct. In hives I looked - * at, it has value 0, 0xc, 0x10, 0x18, 0x1a, 0x28. - *) - unknown3 : 4*8 : littleendian; - (* sentinelchicken.com says: maximum number of bytes in a value - * name, however that does not seem to be correct. We think it is - * the maximum number of bytes in the UTF16-LE encoded version of - * the value names (since value names are usually ASCII, that would - * be max length of names * 2). This is a historical maximum, so - * it can be greater than the current maximum name field. - *) - max_vk_name_len : 4*8 : littleendian, bind (Int32.to_int max_vk_name_len); - (* sentinelchicken.com says: maximum value data size, and this - * agrees with my observations. It is the largest data size (not - * seg_len, but vk.data_len) for any value in this key. We think - * that this field is a historical max, so eg if a maximally sized - * value is deleted then this field is not reduced. Certainly - * max_vk_data_len >= the measured maximum in all the hives that we - * have observed. - *) - max_vk_data_len : 4*8 : littleendian, bind (Int32.to_int max_vk_data_len); - unknown6 : 4*8 : littleendian; - name_len : 2*8 : littleendian; - classname_len : 2*8 : littleendian; - name : name_len * 8 : string } - -let fprintf_nk chan nk = - let (_, _, bits) = lookup "fprintf_nk" nk in - bitmatch bits with - | { :nk_fields } -> - fprintf chan - "NK %s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s %s %08lx %s %d %ld %s %08lx %d %s %s %s %d %x %x %x %08lx %d %d %08lx %d %d %s\n" - (print_offset nk) - (if unknownflag8000 then "8" else ".") - (if unknownflag4000 then "4" else ".") - (if unknownflag2000 then "2" else ".") - (if unknownflag1000 then "1" else ".") - (if unknownflag0800 then "8" else ".") - (if unknownflag0400 then "4" else ".") - (if virtualstore then "s" else ".") - (if virttarget then "t" else ".") - (if virtmirrored then "m" else ".") - (if predefinedhandle then "P" else ".") - (if keynameascii then "A" else ".") - (if symlinkkey then "S" else ".") - (if cannotbedeleted then "N" else ".") - (if isroot then "R" else ".") - (if ismountpoint then "M" else ".") - (if isvolatile then "V" else ".") - (print_time timestamp) - unknown1 (print_offset parent) nr_subkeys nr_subkeys_vol - (print_offset subkeys) subkeys_vol - nr_values (print_offset vallist) - (print_offset sk) (print_offset classname) - max_subkey_name_len - unknown2_userflags unknown2_virtcontrolflags unknown2_debug - unknown3 max_vk_name_len max_vk_data_len unknown6 - name_len classname_len name - -type data_t = Inline of bitstring | Offset of int -let bitmatch vk_fields = - { "vk" : 2*8 : string; - name_len : 2*8 : littleendian; - (* Top bit set means that the data is stored inline. In that case - * the data length must be <= 4. The length can also be 0 (or - * 0x80000000) if the data type is NONE. - *) - data_len : 4*8 - : littleendian, bind ( - let is_inline = Int32.logand data_len 0x8000_0000_l = 0x8000_0000_l in - let data_len = Int32.to_int (Int32.logand data_len 0x7fff_ffff_l) in - if is_inline then assert (data_len <= 4) else assert (data_len > 4); - is_inline, data_len - ); - (* The data itself depends on the type field. - * - * For REG_SZ type, the data always seems to be NUL-terminated, which - * means because these strings are often UTF-16LE, that the string will - * end with \0\0 bytes. The termination bytes are included in data_len. - * - * For REG_MULTI_SZ, see - * http://blogs.msdn.com/oldnewthing/archive/2009/10/08/9904646.aspx - *) - data : 4*8 - : bitstring, bind ( - let is_inline, data_len = data_len in - if is_inline then - Inline (takebits (data_len*8) data) - else ( - let offset = - bitmatch data with { offset : 4*8 : littleendian } -> offset in - let offset = get_offset offset in - Offset offset - ) - ); - t : 4*8 : littleendian, bind (Int32.to_int t); - (* Flags, stored as a little-endian word: *) - unknown1 : 7; - nameisascii : 1; (* Clear for default [zero-length] name, always set - * otherwise in registries that we found. Perhaps this - * is really "nameisdefault" flag? - *) - unknown2 : 8; - (* Unknown field, usually contains something. *) - unknown3 : 2*8 : littleendian; - name : name_len * 8 : string } - -let fprintf_vk chan vk = - let (_, _, bits) = lookup "fprintf_vk" vk in - bitmatch bits with - | { :vk_fields } -> - let real_data = - match data with - | Inline data -> data - | Offset offset -> - let (_, _, bits) = lookup "fprintf_vk (data)" offset in - bits in - let is_inline, data_len = data_len in - fprintf chan "VK %s %s %s %d %s%s %s %08x %s %08x %08x\n" - (print_offset vk) - name (if is_inline then "inline" else "-") data_len - (match data with - | Inline _ -> "" - | Offset offset -> "["^print_offset offset^"]") - (print_bitstring real_data) - (print_vk_type t) - unknown1 (if nameisascii then "A" else "L") - unknown2 unknown3 - -let bitmatch sk_fields = - { "sk" : 2*8 : string; - unknown1 : 2*8 : littleendian; - sk_next : 4*8 : littleendian, bind (get_offset sk_next); - sk_prev : 4*8 : littleendian, bind (get_offset sk_prev); - refcount : 4*8 : littleendian, bind (Int32.to_int refcount); - sec_len : 4*8 : littleendian, bind (Int32.to_int sec_len); - sec_desc : sec_len * 8 : bitstring } - -let fprintf_sk chan sk = - let (_, _, bits) = lookup "fprintf_sk" sk in - bitmatch bits with - | { :sk_fields } -> - fprintf chan "SK %s %04x %s %s %d %d\n" - (print_offset sk) unknown1 - (print_offset sk_next) (print_offset sk_prev) - refcount sec_len - (* print_bitstring sec_desc -- suppress this *) - -(* Store lists of records we encounter (lists of offsets). *) -let nk_records = ref [] -and vk_records = ref [] -and sk_records = ref [] - -(* Functions to visit each block, starting at the root. Each block - * that we visit is printed. - *) -let rec visit_nk ?(nk_is_root = false) nk = - let (_, _, bits) = lookup "visit_nk" nk in - mark_visited nk; - (bitmatch bits with - | { :nk_fields } -> - fprintf_nk stdout nk; - - nk_records := nk :: !nk_records; - - (* Check the isroot flag is only set on the root node. *) - assert (isroot = nk_is_root); - - if unknownflag8000 then - eprintf "NK %s unknownflag8000 is set\n" (print_offset nk); - if unknownflag4000 then - eprintf "NK %s unknownflag4000 is set\n" (print_offset nk); - if unknownflag2000 then - eprintf "NK %s unknownflag2000 is set\n" (print_offset nk); - if unknownflag1000 then - eprintf "NK %s unknownflag1000 is set\n" (print_offset nk); - if unknownflag0800 then - eprintf "NK %s unknownflag0800 is set\n" (print_offset nk); - if unknownflag0400 then - eprintf "NK %s unknownflag0400 is set\n" (print_offset nk); - if unknown1 <> 0_l then - eprintf "NK %s unknown1 <> 0 (%08lx)\n" (print_offset nk) unknown1; - if unknown2_userflags <> 0 then - eprintf "NK %s unknown2_userflags <> 0 (%x)\n" - (print_offset nk) unknown2_userflags; - if unknown2_virtcontrolflags <> 0 then - eprintf "NK %s unknown2_virtcontrolflags <> 0 (%x)\n" - (print_offset nk) unknown2_virtcontrolflags; - if unknown2_debug <> 0 then - eprintf "NK %s unknown2_debug <> 0 (%x)\n" - (print_offset nk) unknown2_debug; - if unknown3 <> 0_l then - eprintf "NK %s unknown3 <> 0 (%08lx)\n" (print_offset nk) unknown3; - if unknown6 <> 0_l then - eprintf "NK %s unknown6 <> 0 (%08lx)\n" (print_offset nk) unknown6; - - (* -- common, assume it's not an error - if classname = -1 then - eprintf "NK %s has no classname\n" (print_offset nk); - if classname_len = 0 then - eprintf "NK %s has zero-length classname\n" (print_offset nk); - *) - if sk = -1 then - eprintf "NK %s has no sk-record\n" (print_offset nk); - if name_len = 0 then - eprintf "NK %s has zero-length name\n" (print_offset nk); - - (* Visit the values first at this node. *) - let max_data_len, max_name_len = - if vallist <> -1 then - visit_vallist nr_values vallist - else - 0, 0 in - - if max_vk_data_len < max_data_len then - eprintf "NK %s nk.max_vk_data_len (%d) < actual max data_len (%d)\n" - (print_offset nk) max_vk_data_len max_data_len; - - if max_vk_name_len < max_name_len * 2 then - eprintf "NK %s nk.max_vk_name_len (%d) < actual max name_len * 2 (%d)\n" - (print_offset nk) max_vk_name_len (max_name_len * 2); - - (* Visit the subkeys of this node. *) - if subkeys <> -1 then ( - let counted, max_name_len, _ = visit_subkeys subkeys in - - if counted <> nr_subkeys then - failwithf "%s: incorrect count of subkeys (%d, counted %d) in subkey list at %s\n" - basename nr_subkeys counted (print_offset subkeys); - - if max_subkey_name_len < max_name_len * 2 then - eprintf "NK %s nk.max_subkey_name_len (%d) < actual max name_len * 2 (%d)\n" - (print_offset nk) max_subkey_name_len (max_name_len * 2); - ); - - (* Visit the sk-record and classname. *) - if sk <> -1 then - visit_sk sk; - if classname <> -1 then - visit_classname classname classname_len; - - | {_} -> - failwithf "%s: invalid nk block at offset %s\n" - basename (print_offset nk) - ) - -and visit_vallist nr_values vallist = - let (seg_len, _, bits) = lookup "visit_vallist" vallist in - mark_visited vallist; - printf "VL %s %d %d\n" (print_offset vallist) nr_values seg_len; - visit_values_in_vallist nr_values vallist bits - -and visit_values_in_vallist nr_values vallist bits = - if nr_values > 0 then ( - bitmatch bits with - | { rest : -1 : bitstring } when bitstring_length rest = 0 -> - assert (nr_values = 0); - 0, 0 - - | { value : 4*8 : littleendian, bind (get_offset value); - rest : -1 : bitstring } -> - let data_len, name_len = visit_vk value in - let max_data_len, max_name_len = - visit_values_in_vallist (nr_values-1) vallist rest in - max max_data_len data_len, max max_name_len name_len - - | {_} -> - failwithf "%s: invalid offset in value list at %s\n" - basename (print_offset vallist) - ) else 0, 0 - -and visit_vk vk = - let (_, _, bits) = lookup "visit_vk" vk in - mark_visited vk; - - (bitmatch bits with - | { :vk_fields } -> - fprintf_vk stdout vk; - - let is_inline, data_len = data_len in - - if unknown1 <> 0 then - eprintf "VK %s unknown1 flags set (%02x)\n" - (print_offset vk) unknown1; - if unknown2 <> 0 then - eprintf "VK %s unknown2 flags set (%02x)\n" - (print_offset vk) unknown2; - if unknown3 <> 0 then - eprintf "VK %s unknown3 flags set (%04x)\n" - (print_offset vk) unknown3; - - (* Note this is common for default [ie. zero-length] key names. *) - if not nameisascii && name_len > 0 then - eprintf "VK %s has non-ASCII name flag set (name is %s)\n" - (print_offset vk) (print_binary_string name); - - vk_records := vk :: !vk_records; - (match data with - | Inline data -> () - | Offset offset -> - let _ = lookup "visit_vk (data)" offset in - mark_visited offset - ); - - data_len, name_len - - | {_} -> - failwithf "%s: invalid vk block at offset %s\n" - basename (print_offset vk) - ) - -(* Visits subkeys, recursing through intermediate lf/lh/ri structures, - * and returns the number of subkeys actually seen. - *) -and visit_subkeys subkeys = - let (_, _, bits) = lookup "visit_subkeys" subkeys in - mark_visited subkeys; - (bitmatch bits with - | { "lf" : 2*8 : string; - len : 2*8 : littleendian; (* number of subkeys of this node *) - rest : len*8*8 : bitstring } -> - printf "LF %s %d\n" (print_offset subkeys) len; - visit_subkeys_in_lf_list false subkeys len rest - - | { "lh" : 2*8 : string; - len : 2*8 : littleendian; (* number of subkeys of this node *) - rest : len*8*8 : bitstring } -> - printf "LF %s %d\n" (print_offset subkeys) len; - visit_subkeys_in_lf_list true subkeys len rest - - | { "ri" : 2*8 : string; - len : 2*8 : littleendian; - rest : len*4*8 : bitstring } -> - printf "RI %s %d\n" (print_offset subkeys) len; - visit_subkeys_in_ri_list subkeys len rest - - (* In theory you can have an li-record here, but we've never - * seen one. - *) - - | { "nk" : 2*8 : string } -> - visit_nk subkeys; - let name, name_len = name_of_nk subkeys in - 1, name_len, name - - | {_} -> - failwithf "%s: invalid subkey node found at %s\n" - basename (print_offset subkeys) - ) - -and visit_subkeys_in_lf_list newstyle_hash subkeys_top len bits = - if len > 0 then ( - bitmatch bits with - | { rest : -1 : bitstring } when bitstring_length rest = 0 -> - assert (len = 0); - 0, 0, "" - - | { offset : 4*8 : littleendian, bind (get_offset offset); - hash : 4*8 : bitstring; - rest : -1 : bitstring } -> - let c1, name_len1, name = visit_subkeys offset in - - check_hash offset newstyle_hash hash name; - - let c2, name_len2, _ = - visit_subkeys_in_lf_list newstyle_hash subkeys_top (len-1) rest in - c1 + c2, max name_len1 name_len2, "" - - | {_} -> - failwithf "%s: invalid subkey in lf/lh list at %s\n" - basename (print_offset subkeys_top) - ) else 0, 0, "" - -and visit_subkeys_in_ri_list subkeys_top len bits = - if len > 0 then ( - bitmatch bits with - | { rest : -1 : bitstring } when bitstring_length rest = 0 -> - assert (len = 0); - 0, 0, "" - - | { offset : 4*8 : littleendian, bind (get_offset offset); - rest : -1 : bitstring } -> - let c1, name_len1, _ = visit_subkeys offset in - let c2, name_len2, _ = - visit_subkeys_in_ri_list subkeys_top (len-1) rest in - c1 + c2, max name_len1 name_len2, "" - - | {_} -> - failwithf "%s: invalid subkey in ri list at %s\n" - basename (print_offset subkeys_top) - ) else 0, 0, "" - -and check_hash offset newstyle_hash hash name = - if not newstyle_hash then ( - (* Old-style lf record hash the first four bytes of the name - * as the has. - *) - let len = String.length name in - let name_bits = - if len >= 4 then - bitstring_of_string (String.sub name 0 4) - else ( - let zeroes = zeroes_bitstring ((4-len)*8) in - concat [bitstring_of_string name; zeroes] - ) in - if not (equals hash name_bits) then - eprintf "LF incorrect hash for name %s, expected %s, actual %s\n" - name (print_bitstring name_bits) (print_bitstring hash) - ) else ( - (* New-style lh record has a proper hash. *) - let actual = bitmatch hash with { hash : 4*8 : littleendian } -> hash in - let h = ref 0_l in - String.iter ( - fun c -> - h := Int32.mul !h 37_l; - h := Int32.add !h (Int32.of_int (Char.code (Char.uppercase c))) - ) name; - if actual <> !h then - eprintf "LH incorrect hash for name %s, expected 0x%08lx, actual 0x%08lx\n" - name !h actual - ) - -and name_of_nk nk = - let (_, _, bits) = lookup "name_of_nk" nk in - bitmatch bits with - | { :nk_fields } -> name, name_len - -and visit_sk sk = - let (_, _, bits) = lookup "visit_sk" sk in - if is_not_visited sk then ( - mark_visited sk; - (bitmatch bits with - | { :sk_fields } -> - fprintf_sk stdout sk; - - if unknown1 <> 0 then - eprintf "SK %s unknown1 <> 0 (%04x)\n" (print_offset sk) unknown1; - - sk_records := sk :: !sk_records - - | {_} -> - failwithf "%s: invalid sk-record at %s\n" - basename (print_offset sk) - ) - ) - -and visit_classname classname classname_len = - let (seg_len, _, bits) = lookup "visit_classname" classname in - mark_visited classname; - assert (seg_len >= classname_len); - printf "CL %s %s\n" (print_offset classname) (print_bitstring bits) - -let () = - visit_nk ~nk_is_root:true root_key - -(* These are immutable now. *) -let nk_records = !nk_records -let vk_records = !vk_records -let sk_records = !sk_records - -(* So we can rapidly tell what is an nk/vk/sk offset. *) -let nk_set = - List.fold_left (fun set offs -> IntSet.add offs set) IntSet.empty nk_records -let vk_set = - List.fold_left (fun set offs -> IntSet.add offs set) IntSet.empty vk_records -let sk_set = - List.fold_left (fun set offs -> IntSet.add offs set) IntSet.empty sk_records - -(* Now after visiting all the blocks, are there any used blocks which - * are unvisited? If there are any then that would indicate either (a) - * that the hive contains unreferenced blocks, or (b) that there are - * referenced blocks that we did not visit because we don't have a full - * understanding of the hive format. - * - * Windows 7 registries often contain a few of these -- not clear - * how serious they are, but don't fail here. - *) -let () = - let unvisited = unvisited_blocks () in - IntMap.iter ( - fun offset block -> - match block with - | (_, false, _) -> () (* ignore unused blocks *) - | (seg_len, true, _) -> - eprintf "used block %s (length %d) is not referenced\n" - (print_offset offset) seg_len - ) unvisited - -(* Check the SKs are: - * (a) linked into a single circular list through the sk_prev/sk_next - * pointers - * (b) refcounts are correct - *) -let () = - if List.length sk_records > 0 then ( - let sk0 = List.hd sk_records in (* start at any arbitrary sk *) - (* This loop follows the chain of sk pointers until we arrive - * back at the original, checking prev/next are consistent. - *) - let rec loop visited prevsk sk = - if sk <> sk0 then ( - if not (IntSet.mem sk sk_set) then - eprintf "SK %s not an sk-record (faulty sk_next somewhere)\n" - (print_offset sk) - else ( - let _, _, bits = lookup "loop sk circular list" sk in - bitmatch bits with - | { :sk_fields } -> - if sk_prev <> prevsk then - eprintf "SK %s sk_prev != previous sk (%s, %s)\n" - (print_offset sk) - (print_offset sk_prev) (print_offset prevsk); - if IntSet.mem sk visited then - eprintf "SK %s already visited (bad circular list)\n" - (print_offset sk); - let visited = IntSet.add sk visited in - loop visited sk sk_next - ) - ) - in - let _, _, bits = lookup "start sk circular list" sk0 in - (bitmatch bits with - | { :sk_fields } -> - loop IntSet.empty sk_prev sk0 - ); - - (* For every nk-record, if it references an sk-record count that, - * then check this matches the refcounts in the sk-records - * themselves. - *) - let refcounts = Counter.create () in - List.iter ( - fun nk -> - let _, _, bits = lookup "sk refcounter (nk)" nk in - (bitmatch bits with - | { :nk_fields } -> - Counter.incr refcounts sk - ) - ) nk_records; - - List.iter ( - fun sk -> - let _, _, bits = lookup "sk refcounter (sk)" sk in - (bitmatch bits with - | { :sk_fields } -> - let actual = Counter.get refcounts sk in - if actual <> refcount then - eprintf "SK %s incorrect refcount (actual %d, in file %d)\n" - (print_offset sk) actual refcount - ) - ) sk_records - ) diff --git a/hivex/tools/visualizer_NT_time.ml b/hivex/tools/visualizer_NT_time.ml deleted file mode 100644 index a7521121..00000000 --- a/hivex/tools/visualizer_NT_time.ml +++ /dev/null @@ -1,30 +0,0 @@ -(* Windows Registry reverse-engineering tool. - * Copyright (C) 2010 Red Hat Inc. - * - * 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. - * - * For existing information on the registry format, please refer - * to the following documents. Note they are both incomplete - * and inaccurate in some respects. - *) - -(* Convert an NT file timestamp to time_t. See: - * http://blogs.msdn.com/oldnewthing/archive/2003/09/05/54806.aspx - * http://support.microsoft.com/kb/167296 - *) -let nt_to_time_t t = - let t = Int64.sub t 116444736000000000L in - let t = Int64.div t 10000000L in - Int64.to_float t diff --git a/hivex/tools/visualizer_utils.ml b/hivex/tools/visualizer_utils.ml deleted file mode 100644 index 2f0d6b7d..00000000 --- a/hivex/tools/visualizer_utils.ml +++ /dev/null @@ -1,163 +0,0 @@ -(* Windows Registry reverse-engineering tool. - * Copyright (C) 2010 Red Hat Inc. - * - * 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. - * - * For existing information on the registry format, please refer - * to the following documents. Note they are both incomplete - * and inaccurate in some respects. - *) - -open ExtString -open Printf - -let failwithf fs = ksprintf failwith fs - -(* Useful function to convert unknown bitstring fragments into - * printable strings. - *) -let rec print_bitstring bits = - let str = Bitstring.string_of_bitstring bits in - print_binary_string str -and print_binary_string str = - let rec printable = function - | '\x00' -> "\\0" | '\x01' -> "\\1" | '\x02' -> "\\2" | '\x03' -> "\\3" - | '\x04' -> "\\4" | '\x05' -> "\\5" | '\x06' -> "\\6" | '\x07' -> "\\7" - | ('\x08'..'\x31' as c) - | ('\x7f'..'\xff' as c) -> sprintf "\\x%02x" (Char.code c) - | ('\x32'..'\x7e' as c) -> String.make 1 c - and repeat str = function - | n when n <= 0 -> "" - | n -> str ^ repeat str (n-1) - in - let chars = String.explode str in - let rec loop acc = function - | [] -> List.rev acc - | x :: xs -> - let rec loop2 i = function - | y :: ys when x = y -> loop2 (i+1) ys - | ys -> i, ys - in - let count, ys = loop2 1 xs in - let acc = (count, x) :: acc in - loop acc ys - in - let frags = loop [] chars in - let frags = - List.map (function - | (nr, x) when nr <= 4 -> repeat (printable x) nr - | (nr, x) -> sprintf "%s<%d times>" (printable x) nr - ) frags in - "\"" ^ String.concat "" frags ^ "\"" - -(* Convert an offset from the file to an offset. The only special - * thing is that 0xffffffff in the file is used as a kind of "NULL - * pointer". We map these null values to -1. - *) -let get_offset = function - | 0xffffffff_l -> -1 - | i -> Int32.to_int i - -(* Print an offset. *) -let print_offset = function - | -1 -> "NULL" - | i -> sprintf "@%08x" i - -(* Print time. *) -let print_time t = - let tm = Unix.gmtime t in - sprintf "%04d-%02d-%02d %02d:%02d:%02d" - (tm.Unix.tm_year + 1900) (tm.Unix.tm_mon + 1) tm.Unix.tm_mday - tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec - -(* Print UTF16LE. *) -let print_utf16 str = - let n = String.length str in - if n land 1 <> 0 then - print_binary_string str - else ( - let rec loop i = - if i < n-1 then ( - let c1 = Char.code (str.[i]) in - let c2 = Char.code (str.[i+1]) in - if c1 <> 0 || c2 <> 0 then ( - (* Well, this doesn't print non-7bit-ASCII ... *) - let c = - if c2 = 0 then String.make 1 (Char.chr c1) - else sprintf "\\u%04d" (c2 * 256 + c1) in - c :: loop (i+2) - ) else [] - ) else [] - in - let frags = loop 0 in - "L\"" ^ String.concat "" frags ^ "\"" - ) - -(* A map of int -> anything. *) -module IntMap = Map.Make (struct type t = int let compare = compare end) - -(* A set of ints. *) -module IntSet = Set.Make (struct type t = int let compare = compare end) - -(* Print registry vk-record type field. *) -let print_vk_type = function - | 0 -> "NONE" - | 1 -> "SZ" - | 2 -> "EXPAND_SZ" - | 3 -> "BINARY" - | 4 -> "DWORD" - | 5 -> "DWORD_BIG_ENDIAN" - | 6 -> "LINK" - | 7 -> "MULTI_SZ" - | 8 -> "RESOURCE_LiST" - | 9 -> "FULL_RESOURCE_DESCRIPTOR" - | 10 -> "RESOURCE_REQUIREMENTS_LIST" - | 11 -> "QWORD" - | i -> sprintf "UNKNOWN_VK_TYPE_%d" i - -(* XXX We should write a more efficient version of this and - * push it into the bitstring library. - *) -let is_zero_bitstring bits = - let len = Bitstring.bitstring_length bits in - let zeroes = Bitstring.zeroes_bitstring len in - 0 = Bitstring.compare bits zeroes - -let is_zero_guid = is_zero_bitstring - -(* http://msdn.microsoft.com/en-us/library/aa373931(VS.85).aspx - * Endianness of GUIDs is not clear from the MSDN documentation, - * so this is just a guess. - *) -let print_guid bits = - bitmatch bits with - | { data1 : 4*8 : littleendian; - data2 : 2*8 : littleendian; - data3 : 2*8 : littleendian; - data4_1 : 2*8 : littleendian; - data4_2 : 6*8 : littleendian } -> - sprintf "%08lX-%04X-%04X-%04X-%012LX" data1 data2 data3 data4_1 data4_2 - | { _ } -> - assert false - -(* Fold over little-endian 32-bit integers in a bitstring. *) -let rec bitstring_fold_left_int32_le f a bits = - bitmatch bits with - | { i : 4*8 : littleendian; - rest : -1 : bitstring } -> - bitstring_fold_left_int32_le f (f a i) rest - | { rest : -1 : bitstring } when Bitstring.bitstring_length rest = 0 -> a - | { _ } -> - invalid_arg "bitstring_fold_left_int32_le: length not a multiple of 32 bits" diff --git a/po/POTFILES.in b/po/POTFILES.in index c6dbd032..91021062 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -80,9 +80,6 @@ fish/tilde.c fish/time.c fuse/dircache.c fuse/guestmount.c -hivex/hivex.c -hivex/hivexml.c -hivex/hivexsh.c inspector/virt-inspector java/com_redhat_et_libguestfs_GuestFS.c ocaml/guestfs_c.c diff --git a/src/guestfs.pod b/src/guestfs.pod index 514db030..2e608c6e 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -450,10 +450,10 @@ Where we can help is in resolving the case insensitivity of paths. For this, call C<guestfs_case_sensitive_path>. Libguestfs also provides some help for decoding Windows Registry -"hive" files, through the library C<libhivex> which is part of -libguestfs. You have to locate and download the hive file(s) -yourself, and then pass them to C<libhivex> functions. See also the -programs L<hivexml(1)>, L<hivexget(1)> and L<virt-win-reg(1)> for more +"hive" files, through the library C<hivex> which is part of the +libguestfs project. You have to locate and download the hive file(s) +yourself, and then pass them to C<hivex> functions. See also the +programs L<hivexml(1)>, L<hivexsh(1)> and L<virt-win-reg(1)> for more help on this issue. =head2 USING LIBGUESTFS WITH OTHER PROGRAMMING LANGUAGES diff --git a/tools/run-locally b/tools/run-locally index eb55d181..cb3dca86 100755 --- a/tools/run-locally +++ b/tools/run-locally @@ -48,7 +48,6 @@ while(-l $path) { # Get the absolute path of the parent directory $path = abs_path(dirname($path).'/..'); -$ENV{PATH} = $path.'/hivex:'.$ENV{PATH}; $ENV{LD_LIBRARY_PATH} = $path.'/src/.libs'; $ENV{LIBGUESTFS_PATH} = $path.'/appliance'; $ENV{PERL5LIB} = $path.'/perl/blib/lib:'.$path.'/perl/blib/arch'; |