[Forensics-changes] [SCM] debian-forensics/reglookup branch, debian, updated. debian/0.11.0-2-4-g672c432
Christophe Monniez
christophe.monniez at fccu.be
Mon Mar 22 08:18:39 UTC 2010
The following commit has been merged in the debian branch:
commit 958bc6c8c9dd712eac46a42243ef64323ed6228f
Author: Christophe Monniez <christophe.monniez at fccu.be>
Date: Sat Mar 20 11:28:02 2010 +0100
Merging upstream version 0.12.0.
diff --git a/Makefile b/Makefile
index e22d83c..a19a61f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile 147 2009-02-22 19:31:52Z tim $
+# $Id: Makefile 159 2009-12-06 20:09:01Z tim $
# Installation prefixes. Change to install elsewhere.
@@ -10,8 +10,8 @@ MAN_PREFIX=$(PREFIX)/man
################################################################################
CC=gcc
-OPTS=-std=gnu89 -pedantic -Wall -ggdb
-#OPTS=-std=gnu89 -pedantic -Wall
+OPTS=-std=gnu99 -pedantic -Wall -ggdb
+#OPTS=-std=gnu99 -pedantic -Wall
INC:=-I$(PWD)/include -I/usr/local/include
LIB=-L/usr/local/lib -lm
BIN_EXT=
diff --git a/bin/reglookup-timeline b/bin/reglookup-timeline
index cd8ab4d..67c7eaa 100755
--- a/bin/reglookup-timeline
+++ b/bin/reglookup-timeline
@@ -4,7 +4,7 @@
# files to produce an MTIME sorted output. This is helpful when building
# timelines for investigations.
#
-# Copyright (C) 2005-2007 Timothy D. Morgan
+# Copyright (C) 2005-2007,2010 Timothy D. Morgan
#
# 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
@@ -19,13 +19,14 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
-# $Id: reglookup-timeline 91 2007-03-28 19:26:37Z tim $
+# $Id: reglookup-timeline 170 2010-03-06 04:40:25Z tim $
usage()
{
- echo "Usage: $0 [-H] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
+ echo "Usage: $0 [-H] [-V] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
echo " -H Omit header line" 1>&2
+ echo " -V Include values with parent timestamps" 1>&2
}
if [ $# -eq 0 ]; then
@@ -40,10 +41,16 @@ if [ "$1" = "-H" ]; then
shift
fi
+OPTS='-t KEY'
+if [ "$1" = "-V" ]; then
+ OPTS='-i'
+ shift
+fi
+
if [ "$PRINT_HEADER" = "true" ]; then
echo "MTIME,FILE,PATH"
fi
for F in $@; do
- reglookup -t KEY -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
+ reglookup $OPTS -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
done | sort
diff --git a/doc/devel/README b/doc/devel/README
new file mode 100644
index 0000000..2960276
--- /dev/null
+++ b/doc/devel/README
@@ -0,0 +1,4 @@
+Developer's documentation previously stored here has been removed from the
+main software distribution trunk. It is still available in the Subversion
+repository, however, and may be obtained here:
+ http://code.google.com/p/reglookup/source/browse/
diff --git a/doc/devel/TODO b/doc/devel/TODO
deleted file mode 100644
index 549fac8..0000000
--- a/doc/devel/TODO
+++ /dev/null
@@ -1,57 +0,0 @@
-$Id: TODO 153 2009-06-02 22:28:07Z tim $
-
-If you are interested in contributing to this project, here's a few
-things you could look into:
-
- - Currently there is no way on the command line to search for exotic
- paths/types. For instance, if reglookup encounters an unknown VK
- type, it just prints it out in Hex. However, if you wanted to search
- specifically for that type, there is no way to do it. Similarly, it
- isn't possible to specify certain binary or weird characters in
- paths. Reglookup should take the user path and unquote each path
- component using the \xQQ syntax prior to searching.
-
- - It might be nice to have a way to filter results by security
- descriptor information. Maybe by MTIME as well.
-
- - Testing, testing, and more testing. reglookup needs to be tested on
- NT/XP/2k3/Vista. A regression test suite would be nice too. Some
- thoughts on this include a script which randomly fuzzes an existing
- registry file, and tries to detect crashes of reglookup when parsing
- it. Another test script might randomly truncate an existing registry
- file, which will help improve reglookup's parsing on fragmentary
- files.
-
- - Build system. I do not wish to use automake/autoconf in this
- project. I have also now decided that make is painful to use for
- everything. I'd like to switch to a suite of shell scripts driven by
- minimal make rules. If you got any ideas on this, shoot them my way.
-
- - Unicode support still needs improvement. While parsing strings seems
- to be decent, UTF-8 output would be nice. Need support for
- UTF-16LE key and value names. To do this, the UTF conversion
- functions need to be moved to regfi.
-
- - The interface between reglookup.c and regfi.c is much better than it
- used to be, but the parsing of data objects needs to be moved into the
- library. The quoting syntax should stay in reglookup/reglookup-recover
- but the basic parsing of data types into proper structures should
- happen in the library so that they are accessible to other users of the
- library.
-
- - Develop and solidify regfi API. Regfi should be better documented and
- eventually needs a set of Python wrappers.
-
- - Documentation. The security descriptor output format needs to be
- documented. Also, function contracts should be added to the
- lower-level functions of regfi.c.
-
- - The smb_deps.h and smb_deps.c content is almost eliminated. Just need
- to integrate parts that are being kept into regfi or other modules.
-
- - Need to figure out a reasonably correct way to convert UTF-16LE charaters
- to ASCII under Windows/MingW or other platforms that don't have proper
- libiconv support yet. Then a build-time option or autodetection can
- dictate which version of conversion function is used.
-
- - Grep through the source for 'XXX', and you'll find more.
diff --git a/doc/devel/references.txt b/doc/devel/references.txt
deleted file mode 100644
index 457bfa9..0000000
--- a/doc/devel/references.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-- The Windows NT Registry File Format
- (A work in progress to support this tool.)
- http://sentinelchicken.com/research/registry_format/
-
-- Recovering Deleted Data From the Windows Registry
- (The research that is implemented as a PoC in reglookup-recover.)
- http://sentinelchicken.com/research/registry_recovery/
-
-- Petter Nordahl-Hagen. Windows NT registry file format description.
- (The file 'winntreg.txt' included in this distribution is derived from this.)
- http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt
-
-- Some useful information on how Windows reads from and writes to registry
- hives:
- http://www.microsoft.com/technet/archive/winntas/tips/winntmag/inreg.mspx
-
-- Registry key, value, and depth limits:
- http://msdn2.microsoft.com/en-us/library/ms724872.aspx
-
-- Misc references for windows registry permissions and ownership:
- http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
- http://technet2.microsoft.com/WindowsServer/en/library/86cf2457-4f17-43f8-a2ab-7f4e2e5659091033.mspx?mfr=true
- http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
-
-- ACL/ACE flags information
- http://support.microsoft.com/kb/220167
- http://msdn2.microsoft.com/en-us/library/aa772242.aspx
-
-- Info on SAM hive, syskey, and hash extraction (with tools bkhive and samdump2):
- http://www.studenti.unina.it/~ncuomo/syskey/
diff --git a/doc/devel/winntreg.txt b/doc/devel/winntreg.txt
deleted file mode 100644
index 31bbdf1..0000000
--- a/doc/devel/winntreg.txt
+++ /dev/null
@@ -1,272 +0,0 @@
-The windows NT registry has 2 different blocks, where one can occur many
-times...
-
-the "regf"-Block
-================
-
-"regf" is obviosly the abbreviation for "Registry file". "regf" is the
-signature of the header-block which is always 4kb in size, although only
-the first 64 bytes seem to be used and a checksum is calculated over
-the first 0x200 bytes only!
-
-Offset Size Contents
-0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
-0x00000004 D-Word ???? //see struct REGF
-0x00000008 D-Word ???? Always the same value as at 0x00000004
-0x0000000C Q-Word last modify date in WinNT date-format
-0x00000014 D-Word 1
-0x00000018 D-Word 3
-0x0000001C D-Word 0
-0x00000020 D-Word 1
-0x00000024 D-Word Offset of 1st key record
-0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
-0x0000002C D-Word 1
-0x000001FC D-Word Sum of all D-Words from 0x00000000 to
- 0x000001FB //XOR of all words. Nigel
-
-I have analyzed more registry files (from multiple machines running
-NT 4.0 german version) and could not find an explanation for the values
-marked with ???? the rest of the first 4kb page is not important...
-
-the "hbin"-Block
-================
-I dont know what "hbin" stands for, but this block is always a multiple
-of 4kb in size.
-
-Inside these hbin-blocks the different records are placed. The memory-
-management looks like a C-compiler heap management to me...
-
-hbin-Header
-===========
-Offset Size Contents
-0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
-0x0004 D-Word Offset from the 1st hbin-Block
-0x0008 D-Word Offset to the next hbin-Block
-0x001C D-Word Block-size
-
-The values in 0x0008 and 0x001C should be the same, so I dont know
-if they are correct or swapped...
-
-From offset 0x0020 inside a hbin-block data is stored with the following
-format:
-
-Offset Size Contents
-0x0000 D-Word Data-block size //this size must be a
-multiple of 8. Nigel
-0x0004 ???? Data
-
-If the size field is negative (bit 31 set), the corresponding block
-is free and has a size of -blocksize!
-
-That does not seem to be true. All block lengths seem to be negative!
-(Richard Sharpe)
-
-The data is stored as one record per block. Block size is a multiple
-of 4 and the last block reaches the next hbin-block, leaving no room.
-
-(That also seems incorrect, in that the block size if a multiple of 8.
-That is, the block, including the 4 byte header, is always a multiple of
-8 bytes. Richard Sharpe.)
-
-Records in the hbin-blocks
-==========================
-
-nk-Record
-
- The nk-record can be treated as a kombination of tree-record and
- key-record of the win 95 registry.
-
-lf-Record
-
- The lf-record is the counterpart to the RGKN-record (the
- hash-function)
-
-vk-Record
-
- The vk-record consists information to a single value.
-
-sk-Record
-
- sk (? Security Key ?) is the ACL of the registry.
-
-Value-Lists
-
- The value-lists contain information about which values are inside a
- sub-key and dont have a header.
-
-Datas
-
- The datas of the registry are (like the value-list) stored without a
- header.
-
-All offset-values are relative to the first hbin-block and point to the
-block-size field of the record-entry. to get the file offset, you have to add
-the header size (4kb) and the size field (4 bytes)...
-
-the nk-Record
-=============
-Offset Size Contents
-0x0000 Word ID: ASCII-"nk" = 0x6B6E
-0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
-0x0004 Q-Word write-date/time in windows nt notation
-0x000C D-Word UNKNOWN // added by TDM
-0x0010 D-Word Offset of Owner/Parent key
-0x0014 D-Word number of sub-Keys
-0x0018 D-Word UNKNOWN // added by TDM
-0x001C D-Word Offset of the sub-key lf-Records
-0x0020 D-Word UNKNOWN // added by TDM
-0x0024 D-Word number of values
-0x0028 D-Word Offset of the Value-List
-0x002C D-Word Offset of the sk-Record
-
-0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
-0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
-0x0048 Word name-length
-0x004A Word class-name length
-0x004C ???? key-name
-
-the Value-List
-==============
-Offset Size Contents
-0x0000 D-Word Offset 1st Value
-0x0004 D-Word Offset 2nd Value
-0x???? D-Word Offset nth Value
-
-To determine the number of values, you have to look at the owner-nk-record!
-
-Der vk-Record
-=============
-Offset Size Contents
-0x0000 Word ID: ASCII-"vk" = 0x6B76
-0x0002 Word name length
-0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
-0x0008 D-Word Offset of Data
-0x000C D-Word Type of value
-0x0010 Word Flag
-0x0012 Word Unused (data-trash)
-0x0014 ???? Name
-
-If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
-
-If the data-size is lower 5, the data-offset value is used to store the data itself!
-
-The data-types
-==============
-Wert Beteutung
-0x0001 RegSZ: character string (in UNICODE!)
-0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
-0x0003 RegBin: raw-binary value
-0x0004 RegDWord: Dword
-0x0007 RegMultiSZ: multiple strings, seperated with 0
- (UNICODE!)
-
-The "lf"/"lh"/"ri"-record (hash list header)
-===============
-Offset Size Contents
-0x0000 Word ID: ASCII-"lf" = 0x666C (or "lh" or "ri")
-0x0002 Word number of keys
-0x0004 ???? Hash-Records
-
-Hash-Record
-===========
-Offset Size Contents
-0x0000 D-Word Offset of corresponding "nk"-Record
-0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0-s. Case sensitive!
- (the hash value may be computed differently for the various header types
- "lf"/"lh"/"ri"/etc)
-Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
-key-name you have to change the hash-value too!
-
-//These hashrecords must be sorted low to high within the lf record. Nigel.
-
-The "sk"-block
-==============
-(due to the complexity of the SAM-info, not clear jet)
-(This is just a self-relative security descriptor in the data. R Sharpe.)
-
-
-Offset Size Contents
-0x0000 Word ID: ASCII-"sk" = 0x6B73
-0x0002 Word Unused
-0x0004 D-Word Offset of previous "sk"-Record
-0x0008 D-Word Offset of next "sk"-Record
-0x000C D-Word usage-counter
-0x0010 D-Word Size of "sk"-record in bytes
-???? //standard self
-relative security desciptor. Nigel
-???? ???? Security and auditing settings...
-????
-
-The usage counter counts the number of references to this
-"sk"-record. You can use one "sk"-record for the entire registry!
-
-Windows nt date/time format
-===========================
-The time-format is a 64-bit integer which is incremented every
-0,0000001 seconds by 1 (I dont know how accurate it really is!)
-It starts with 0 at the 1st of january 1601 0:00! All values are
-stored in GMT time! The time-zone is important to get the real
-time!
-
-Common values for win95 and win-nt
-==================================
-Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
-If a value has no name (length=0, flag(bit 0)=0), it is treated as the
-"Default" entry...
-If a value has no data (length=0), it is displayed as empty.
-
-simplyfied win-3.?? registry:
-=============================
-
-+-----------+
-| next rec. |---+ +----->+------------+
-| first sub | | | | Usage cnt. |
-| name | | +-->+------------+ | | length |
-| value | | | | next rec. | | | text |------->+-------+
-+-----------+ | | | name rec. |--+ +------------+ | xxxxx |
- +------------+ | | value rec. |-------->+------------+ +-------+
- v | +------------+ | Usage cnt. |
-+-----------+ | | length |
-| next rec. | | | text |------->+-------+
-| first sub |------+ +------------+ | xxxxx |
-| name | +-------+
-| value |
-+-----------+
-
-Greatly simplyfied structure of the nt-registry:
-================================================
-
-+---------------------------------------------------------------+
-| |
-v |
-+---------+ +---------->+-----------+ +----->+---------+ |
-| "nk" | | | lf-rec. | | | nk-rec. | |
-| ID | | | # of keys | | | parent |---+
-| Date | | | 1st key |--+ | .... |
-| parent | | +-----------+ +---------+
-| suk-keys|-----+
-| values |--------------------->+----------+
-| SK-rec. |---------------+ | 1. value |--> +----------+
-| class |--+ | +----------+ | vk-rec. |
-+---------+ | | | .... |
- v | | data |--> +-------+
- +------------+ | +----------+ | xxxxx |
- | Class name | | +-------+
- +------------+ |
- v
- +---------+ +---------+
- +----->| next sk |--->| Next sk |--+
- | +---| prev sk |<---| prev sk | |
- | | | .... | | ... | |
- | | +---------+ +---------+ |
- | | ^ |
- | | | |
- | +--------------------+ |
- +----------------------------------+
-
----------------------------------------------------------------------------
-
-Hope this helps.... (Although it was *fun* for me to uncover this things,
- it took me several sleepless nights ;)
-
- B.D.
diff --git a/doc/man/man1/reglookup-recover.1.gz b/doc/man/man1/reglookup-recover.1.gz
index b885411..82e8ad0 100644
Binary files a/doc/man/man1/reglookup-recover.1.gz and b/doc/man/man1/reglookup-recover.1.gz differ
diff --git a/doc/man/man1/reglookup-timeline.1.gz b/doc/man/man1/reglookup-timeline.1.gz
index c1c4d3f..ddbb3df 100644
Binary files a/doc/man/man1/reglookup-timeline.1.gz and b/doc/man/man1/reglookup-timeline.1.gz differ
diff --git a/doc/man/man1/reglookup.1.gz b/doc/man/man1/reglookup.1.gz
index 9f14fd4..872fb55 100644
Binary files a/doc/man/man1/reglookup.1.gz and b/doc/man/man1/reglookup.1.gz differ
diff --git a/doc/reglookup.1.docbook b/doc/reglookup.1.docbook
index 9f208ff..7bf3a83 100644
--- a/doc/reglookup.1.docbook
+++ b/doc/reglookup.1.docbook
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<refentry id='reglookup.1'>
- <!-- $Id: reglookup.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
+ <!-- $Id: reglookup.1.docbook 176 2010-03-09 03:10:10Z tim $ -->
<refmeta>
<refentrytitle>reglookup</refentrytitle>
<manvolnum>1</manvolnum>
@@ -85,6 +85,24 @@
</listitem>
</varlistentry>
</variablelist>
+
+ <variablelist remap='IP'>
+ <varlistentry>
+ <term>
+ <option>-i</option>
+ </term>
+ <listitem>
+ <para>
+ Printed values inherit the timestamp of their parent key, which is
+ printed along with them. Note that this timestamp is not
+ necessarily meaningful for any given value values because timestamps
+ are saved on keys only and you cannot tell which value has been
+ modified since a change to any value of a given key would update the
+ time stamp.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
<variablelist remap='IP'>
<varlistentry>
diff --git a/include/byteorder.h b/include/byteorder.h
index 9ae20d7..87cb61b 100644
--- a/include/byteorder.h
+++ b/include/byteorder.h
@@ -1,4 +1,4 @@
-/*
+/*
* Branched from Samba project Subversion repository, version #2:
* http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h
*
@@ -21,15 +21,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: byteorder.h 111 2008-05-01 04:06:22Z tim $
+ * $Id: byteorder.h 168 2010-03-03 00:08:42Z tim $
*/
#ifndef _BYTEORDER_H
#define _BYTEORDER_H
-/*
- This file implements macros for machine independent short and
- int manipulation
+/**
+ * @file
+ *
+ * This file implements macros for machine independent short and
+ * int manipulation
+
+ at verbatim
Here is a description of this file that I emailed to the samba list once:
@@ -53,8 +57,8 @@ CAREFUL_ALIGNMENT=0 on those processors as well.
Ok, now to the macros themselves. I'll take a simple example, say we
want to extract a 2 byte integer from a SMB packet and put it into a
-type called uint16 that is in the local machines byte order, and you
-want to do it with only the assumption that uint16 is _at_least_ 16
+type called uint16_t that is in the local machines byte order, and you
+want to do it with only the assumption that uint16_t is _at_least_ 16
bits long (this last condition is very important for architectures
that don't have any int types that are 2 bytes long)
@@ -64,10 +68,10 @@ You do this:
#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
-then to extract a uint16 value at offset 25 in a buffer you do this:
+then to extract a uint16_t value at offset 25 in a buffer you do this:
char *buffer = foo_bar();
-uint16 xx = SVAL(buffer,25);
+uint16_t xx = SVAL(buffer,25);
We are using the byteoder independence of the ANSI C bitshifts to do
the work. A good optimising compiler should turn this into efficient
@@ -97,6 +101,8 @@ RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
it also defines lots of intermediate macros, just ignore those :-)
+ at endverbatim
+
*/
#undef CAREFUL_ALIGNMENT
@@ -123,37 +129,37 @@ it also defines lots of intermediate macros, just ignore those :-)
#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
-#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
-#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
-#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
-#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
-#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
-#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+#define SVALS(buf,pos) ((int16_t)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
#else /* CAREFUL_ALIGNMENT */
/* this handles things for architectures like the 386 that can handle
alignment errors */
/*
- WARNING: This section is dependent on the length of int16 and int32
+ WARNING: This section is dependent on the length of int16_t and int32_t
being correct
*/
/* get single value from an SMB buffer */
-#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
-#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
-#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
-#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
-#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
-#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
/* store single value in an SMB buffer */
-#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
-#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
-#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
-#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16_t)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
#endif /* CAREFUL_ALIGNMENT */
diff --git a/include/lru_cache.h b/include/lru_cache.h
index 80f77d3..b718bca 100644
--- a/include/lru_cache.h
+++ b/include/lru_cache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
*
* 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
@@ -14,9 +14,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: lru_cache.h 147 2009-02-22 19:31:52Z tim $
+ * $Id: lru_cache.h 169 2010-03-03 19:24:58Z tim $
*/
+/**
+ * @file
+ *
+ * A data structure which approximates a least recently used (LRU) cache.
+ * Implemented as a basic randomized hash table.
+ */
+
+
#ifndef LRU_CACHE_H
#define LRU_CACHE_H
@@ -41,6 +49,8 @@ struct lru_cache_element
lru_cache_element* newer;
};
+
+/** XXX: document this. */
typedef struct _lru_cache
{
uint32_t secret;
@@ -54,26 +64,47 @@ typedef struct _lru_cache
} lru_cache;
+/**
+ * XXX: finish documenting.
+ */
lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
+
+
+/**
+ * XXX: finish documenting.
+ */
lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
uint32_t secret, bool talloc_data);
+
+
+/**
+ * XXX: finish documenting.
+ */
void lru_cache_destroy(lru_cache* ht);
-/*
- *
+
+/**
+ * XXX: finish documenting.
*/
bool lru_cache_update(lru_cache* ht, const void* index,
uint32_t index_len, void* data);
-/* Returns pointer to data previously stored at index.
- * If no data was found at index, NULL is returned.
+/**
+ * XXX: finish documenting.
+ *
+ * @return A pointer to data previously stored at index.
+ * If no data was found at index, NULL is returned.
*/
void* lru_cache_find(lru_cache* ht, const void* index,
uint32_t index_len);
-/* Removes entry from table at index.
- * Returns pointer to data that was there previously.
- * Returns NULL if no entry is at index.
+/**
+ * XXX: finish documenting.
+ *
+ * Removes entry from table at index.
+ *
+ * @return A pointer to data that was there previously or NULL if no entry is
+ * at index.
*/
bool lru_cache_remove(lru_cache* ht, const void* index,
uint32_t index_len);
diff --git a/include/range_list.h b/include/range_list.h
index 942528c..65c4643 100644
--- a/include/range_list.h
+++ b/include/range_list.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
*
* 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
@@ -14,7 +14,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: range_list.h 148 2009-02-22 23:22:59Z tim $
+ * $Id: range_list.h 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
+ *
+ * A data structure which stores a list of address ranges.
+ *
+ * range_lists support basic in-place modifications and maintain the address
+ * space in sorted order. Looking up a range_list_element is implemented
+ * through binary search.
*/
#ifndef _RANGE_LIST_H
@@ -35,6 +45,7 @@ typedef struct _range_list_element
} range_list_element;
+/** XXX: document this. */
typedef struct _range_list
{
range_list_element** elements;
@@ -43,151 +54,127 @@ typedef struct _range_list
} range_list;
-/* range_list_new():
- * Allocates a new range_list.
+/** Allocates a new range_list.
*
- * Returns:
- * A newly allocated range_list, or NULL if an error occurred.
+ * @return A newly allocated range_list, or NULL if an error occurred.
*/
range_list* range_list_new();
-/* range_list_free():
- * Frees the memory associated with a range_list, including the elements, but
- * not any data parameters referenced by those elements. If rl is NULL, does
- * nothing.
+/** Frees the memory associated with a range_list, including the elements, but
+ * not any data parameters referenced by those elements.
+ *
+ * If rl is NULL, does nothing.
*
- * Arguments:
- * rl -- the range_list to be free()d.
+ * @param rl the range_list to be free()d.
*/
void range_list_free(range_list* rl);
-/* range_list_size():
- * Query the current number of elements on a range_list
+/** Query the current number of elements on a range_list
*
- * Arguments:
- * rl -- the range_list to query
+ * @param rl the range_list to query
*
- * Returns:
- * The number of elements currently in the list.
+ * @return The number of elements currently in the list.
*/
uint32_t range_list_size(const range_list* rl);
-/* range_list_add():
- * Adds an element to the range_list.
- * The new element must not overlap with others.
- * NOTE: this is a slow operation.
- *
- * Arguments:
- * rl -- the range list to update
- * offset -- the starting point for the range
- * length -- the length of the range
- * data -- misc data associated with this range element
- * Returns:
- * true on success, false on failure.
- * Failures can occur due to memory limitations, max_size limitations,
- * or if the submitted range overlaps with an existing element. Other
- * errors may also be possible.
+/** Adds an element to the range_list.
+ *
+ * The new element must not overlap with others.
+ * NOTE: this is a slow operation.
+ *
+ * @param rl the range list to update
+ * @param offset the starting point for the range
+ * @param length the length of the range
+ * @param data misc data associated with this range element
+ *
+ * @return true on success, false on failure.
+ *
+ * Failures can occur due to memory limitations, max_size limitations,
+ * or if the submitted range overlaps with an existing element. Other
+ * errors may also be possible.
*/
bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
-/* range_list_remove():
- * Removes an element from the list. The element data structure will be
- * freed, but the data property will not be.
+/** Removes an element from the list.
*
- * Arguments:
- * rl -- the range_list to modify
- * index -- the element index to remove
+ * The element data structure will be freed, but the data property will not be.
*
- * Returns:
- * true if the element was successfully removed, false otherwise.
+ * @param rl the range_list to modify
+ * @param index the element index to remove
+ *
+ * @return true if the element was successfully removed, false otherwise.
*/
bool range_list_remove(range_list* rl, uint32_t index);
-/* range_list_get():
- * Retrieves the element for a given index.
+/** Retrieves the element for a given index.
*
- * Arguments:
- * rl -- the range_list being queried.
- * index -- the element index desired.
+ * @param rl the range_list being queried.
+ * @param index the element index desired.
*
- * Returns:
- * The element for a given index, or NULL if the element is not available.
+ * @return The element for a given index, or NULL if the element is not
+ * available.
*/
const range_list_element* range_list_get(const range_list* rl, uint32_t index);
-/* range_list_find():
- * Attempts to find the unique element whose range encompasses offset.
+/** Attempts to find the unique element whose range encompasses offset.
*
- * Arguments:
- * rl -- the range_list being queried.
- * offset -- the location for which an element is desired.
+ * @param rl the range_list being queried.
+ * @param offset the location for which an element is desired.
*
- * Returns:
- * A matching element index or a negative value if none could be found.
+ * @return A matching element index or a negative value if none could be found.
*/
int32_t range_list_find(const range_list* rl, uint32_t offset);
-/* range_list_find_data():
- * Same as range_list_find(), but returns the data associated with an element.
+/** Same as range_list_find(), but returns the data associated with an element.
*
- * Arguments:
- * rl -- the range_list being queried.
- * offset -- the address to search for in the ranges
+ * @param rl the range_list being queried.
+ * @param offset the address to search for in the ranges
*
- * Returns:
- * The data element of the matching element index or NULL if none could
- * be found.
+ * @return The data element of the matching element index or NULL if none could
+ * be found.
*
- * NOTE: May also return NULL if an element matched but if the data
+ * NOTE: May also return NULL if an element matched but the data
* element was never set.
*/
void* range_list_find_data(const range_list* rl, uint32_t offset);
-/* range_list_split_element():
- * Splits an existing element into two elements in place.
- *
- * The resulting list will contain an additional element whose offset
- * is the one provided and whose length extends to the end of the old element
- * (the one identified by the index). The original element's offset will
- * remain the same while it's length is shortened such that it is contiguous
- * with the newly created element. The newly created element will have an index
- * of one more than the current element.
+/** Splits an existing element into two elements in place.
*
- * Both the original element and the newly created element will reference the
- * original element's data.
+ * The resulting list will contain an additional element whose offset
+ * is the one provided and whose length extends to the end of the old element
+ * (the one identified by the index). The original element's offset will
+ * remain the same while it's length is shortened such that it is contiguous
+ * with the newly created element. The newly created element will have an index
+ * of one more than the current element.
*
- * Arguments:
- * rl -- the range_list to modify
- * index -- the index of the element to be split
- * offset -- the at which the element will be split
+ * Both the original element and the newly created element will reference the
+ * original element's data.
*
- * Returns:
- * true if the element was successfully split, false otherwise.
- *
+ * @param rl the range_list to modify
+ * @param index the index of the element to be split
+ * @param offset the at which the element will be split
*
+ * @return true if the element was successfully split, false otherwise.
*/
bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
-/* range_list_has_range():
- * Determines whether or not a specified range exists contiguously within the
+/** Determines whether or not a specified range exists contiguously within the
* range_list.
*
- * Arguments:
- * rl -- the range_list to search
- * start -- the offset at the beginning of the range
- * length -- the length of the range
+ * @param rl the range_list to search
+ * @param start the offset at the beginning of the range
+ * @param length the length of the range
*
- * Returns:
- * true if the specified range exists and is complete, false otherwise.
+ * @return true if the specified range exists and is complete, false otherwise.
*/
bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
diff --git a/include/regfi.h b/include/regfi.h
index a381560..2f3ae08 100644
--- a/include/regfi.h
+++ b/include/regfi.h
@@ -1,10 +1,5 @@
/*
- * Branched from Samba project Subversion repository, version #6903:
- * http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
- *
- * Windows NT (and later) registry parsing library
- *
- * Copyright (C) 2005-2009 Timothy D. Morgan
+ * Copyright (C) 2005-2010 Timothy D. Morgan
* Copyright (C) 2005 Gerald (Jerry) Carter
*
* This program is free software; you can redistribute it and/or modify
@@ -20,14 +15,44 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: regfi.h 152 2009-06-02 20:00:38Z tim $
+ * $Id: regfi.h 172 2010-03-08 03:04:34Z tim $
+ */
+
+/**
+ * @file
+ * Windows NT (and later) read-only registry library
+ *
+ * This library is intended for use in digital forensics investigations, but
+ * is likely useful in other applications.
+ *
+ * Branched from Samba project Subversion repository, version #6903:
+ * http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
+ */
+
+/**
+ * @mainpage Home
+ *
+ * The regfi library is a read-only NT registry library which serves as the main
+ * engine behind the reglookup tool. It is designed with digital forensic
+ * analysis in mind, but it should also be useful in other tools which need to
+ * efficiently traverse and query registry data structures.
+ *
+ * The library is broken down into four main parts, the
+ * @ref regfiBase "Base Layer", which any code dependent on the library will
+ * likely need to rely on, as well as three main functional layers:
+ * @li @ref regfiIteratorLayer
+ * @li @ref regfiGlueLayer
+ * @li @ref regfiParseLayer
+ *
+ * Most users will find that a combination of the Base Layer and the Iterator Layer
+ * will be sufficient for accessing registry hive files. Those who are wiling
+ * to dive deep into registry data structures, for instance to recover deleted
+ * data structures or to research Windows registry behavior in detail, will
+ * find the Parse Layer to be quite useful.
*/
-/************************************************************
- * Most of this information was obtained from
- * http://www.wednesday.demon.co.uk/dosreg.html
- * Thanks Nigel!
- ***********************************************************/
#ifndef _REGFI_H
#define _REGFI_H
@@ -35,7 +60,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
-#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <time.h>
@@ -43,10 +67,10 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
-#include <assert.h>
+#include <iconv.h>
+#include "byteorder.h"
#include "talloc.h"
-#include "smb_deps.h"
#include "winsec.h"
#include "void_stack.h"
#include "range_list.h"
@@ -59,6 +83,16 @@
#define REGFI_MSG_WARN 0x0004
#define REGFI_MSG_ERROR 0x0010
+typedef uint8_t REGFI_ENCODING;
+/* regfi library supported character encodings */
+#define REGFI_ENCODING_ASCII 0
+#define REGFI_ENCODING_UTF8 1
+#define REGFI_ENCODING_DEFAULT REGFI_ENCODING_ASCII
+/* UTF16LE is not supported for output */
+#define REGFI_ENCODING_UTF16LE 2
+
+#define REGFI_NUM_ENCODINGS 3
+
/* Windows is lame */
#ifdef O_BINARY
#define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
@@ -84,12 +118,23 @@
/* Not a real type in the registry */
#define REG_KEY 0x7FFFFFFF
-#define REGFI_MAX_DEPTH 512
#define REGFI_OFFSET_NONE 0xffffffff
-/* XXX: This is totally arbitrary right now. */
+
+/* This maximum depth is described here:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ */
+#define REGFI_MAX_DEPTH 512
+
+/* This limit defines the maximum number of levels deep that ri subkey list
+ * trees can go.
+ */
+/* XXX: This is totally arbitrary right now.
+ * The actual limit may need to be discovered by experimentation.
+ */
#define REGFI_MAX_SUBKEY_DEPTH 255
+
/* Header sizes and magic number lengths for various records */
#define REGFI_HBIN_ALLOC 0x1000 /* Minimum allocation unit for HBINs */
#define REGFI_REGF_SIZE 0x1000 /* "regf" header block size */
@@ -104,6 +149,7 @@
#define REGFI_VK_MIN_LENGTH 0x14
#define REGFI_SK_MIN_LENGTH 0x14
#define REGFI_SUBKEY_LIST_MIN_LEN 0x4
+#define REGFI_BIG_DATA_MIN_LENGTH 0xC
/* Constants used for validation */
@@ -124,11 +170,7 @@
/* Flags for the vk records */
-/* XXX: This next flag may be incorrect. According to Jeffrey Muir,
-* this may actually indicate that the value name is stored in
-* UTF-16LE.
-*/
-#define REGFI_VK_FLAG_NAME_PRESENT 0x0001
+#define REGFI_VK_FLAG_ASCIINAME 0x0001
#define REGFI_VK_DATA_IN_OFFSET 0x80000000
#define REGFI_VK_MAX_DATA_LENGTH 1024*1024 /* XXX: This is arbitrary */
@@ -139,7 +181,13 @@
#define REGFI_NK_FLAG_UNKNOWN1 0x4000
#define REGFI_NK_FLAG_UNKNOWN2 0x1000
-/* This next one shows up on root keys in some Vista "software" registries */
+/* This next one shows up in some Vista "software" registries */
+/* XXX: This shows up in the following two SOFTWARE keys in Vista:
+ * /Wow6432Node/Microsoft
+ * /Wow6432Node/Microsoft/Cryptography
+ *
+ * It comes along with UNKNOWN2 and ASCIINAME for a total flags value of 0x10A0
+ */
#define REGFI_NK_FLAG_UNKNOWN3 0x0080
/* Predefined handle. Rumor has it that the valuelist count for this key is
@@ -181,25 +229,48 @@
| REGFI_NK_FLAG_HIVE_LINK\
| REGFI_NK_FLAG_VOLATILE\
| REGFI_NK_FLAG_UNKNOWN1\
- | REGFI_NK_FLAG_UNKNOWN2)
+ | REGFI_NK_FLAG_UNKNOWN2\
+ | REGFI_NK_FLAG_UNKNOWN3)
+
+
+#define CHAR_BIT 8
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+ : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+typedef struct _regfi_nttime
+{
+ uint32_t low;
+ uint32_t high;
+} REGFI_NTTIME;
-/* HBIN block */
+
+/** HBIN block information
+ * @ingroup regfiMiddleLayer
+ */
typedef struct _regfi_hbin
{
- uint32 file_off; /* my offset in the registry file */
- uint32 ref_count; /* how many active records are pointing to this
- * block (not used currently)
- */
-
- uint32 first_hbin_off; /* offset from first hbin block */
- uint32 block_size; /* block size of this block
- * Should be a multiple of 4096 (0x1000)
- */
- uint32 next_block; /* relative offset to next block.
- * NOTE: This value may be unreliable!
- */
-
- uint8 magic[REGFI_HBIN_MAGIC_SIZE]; /* "hbin" */
+ /** Offset of this HBIN in the registry file */
+ uint32_t file_off;
+
+ /** Number of active records pointing to this block (not used currently) */
+ uint32_t ref_count;
+
+ /** Offset from first hbin block */
+ uint32_t first_hbin_off;
+
+ /** Block size of this block Should be a multiple of 4096 (0x1000) */
+ uint32_t block_size;
+
+ /** Relative offset to next block.
+ *
+ * @note This value may be unreliable!
+ */
+ uint32_t next_block;
+
+ /** Magic number for the HBIN (should be "hbin"). */
+ uint8_t magic[REGFI_HBIN_MAGIC_SIZE];
} REGFI_HBIN;
@@ -209,132 +280,390 @@ typedef struct
/* Virtual offset of NK record or additional subkey list,
* depending on this list's type.
*/
- uint32 offset;
+ uint32_t offset;
- uint32 hash;
+ uint32_t hash;
} REGFI_SUBKEY_LIST_ELEM;
+/** Subkey-list structure
+ * @ingroup regfiMiddleLayer
+ */
typedef struct _regfi_subkey_list
{
/* Real offset of this record's cell in the file */
- uint32 offset;
+ uint32_t offset;
- uint32 cell_size;
+ uint32_t cell_size;
/* Number of immediate children */
- uint32 num_children;
+ uint32_t num_children;
/* Total number of keys referenced by this list and it's children */
- uint32 num_keys;
+ uint32_t num_keys;
REGFI_SUBKEY_LIST_ELEM* elements;
- uint8 magic[REGFI_CELL_MAGIC_SIZE];
+ uint8_t magic[REGFI_CELL_MAGIC_SIZE];
/* Set if the magic indicates this subkey list points to child subkey lists */
bool recursive_type;
} REGFI_SUBKEY_LIST;
-typedef uint32 REGFI_VALUE_LIST_ELEM;
+typedef uint32_t REGFI_VALUE_LIST_ELEM;
+/** Value-list structure
+ * @ingroup regfiMiddleLayer
+ */
typedef struct _regfi_value_list
{
/* Actual number of values referenced by this list.
* May differ from parent key's num_values if there were parsing errors.
*/
- uint32 num_values;
+ uint32_t num_values;
REGFI_VALUE_LIST_ELEM* elements;
} REGFI_VALUE_LIST;
-/* Value record */
-typedef struct
+/** Class name structure (used in storing SysKeys)
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_classname
{
- uint32 offset; /* Real offset of this record's cell in the file */
- uint32 cell_size; /* ((start_offset - end_offset) & 0xfffffff8) */
+ /** As converted to requested REGFI_ENCODING */
+ char* interpreted;
+
+ /** Represents raw buffer read from classname cell.
+ *
+ * Length of this item is specified in the size field.
+ */
+ uint8_t* raw;
+
+ /** Length of the raw data.
+ *
+ * May be shorter than that indicated by parent key.
+ */
+ uint16_t size;
+} REGFI_CLASSNAME;
+
- uint8* data;
- char* valuename;
- uint16 name_length;
- uint32 hbin_off; /* offset from beginning of this hbin block */
+/** Data record structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_data
+{
+ /** Data type of this data, as indicated by the referencing VK record. */
+ uint32_t type;
+
+ /** Length of the raw data. */
+ uint32_t size;
+
+ /** This is always present, representing the raw data cell contents. */
+ uint8_t* raw;
+
+ /** Represents the length of the interpreted value. Meaning is type-specific. */
+ uint32_t interpreted_size;
+
+ /** These items represent interpreted versions of the REGFI_DATA::raw field.
+ *
+ * Only use the appropriate member according to the REGFI_DATA::type field.
+ * In the event of an unknown type, use only the REGFI_DATA::raw field.
+ */
+ union _regfi_data_interpreted
+ {
+ /** REG_NONE
+ *
+ * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine
+ * length.
+ */
+ uint8_t* none;
+
+ /** REG_SZ
+ *
+ * Stored as a NUL terminated string. Converted to the specified
+ * REGFI_ENCODING.
+ */
+ uint8_t* string;
+
+ /** REG_EXPAND_SZ
+ *
+ * Stored as a NUL terminated string. Converted to the specified
+ * REGFI_ENCODING.
+ */
+ uint8_t* expand_string;
+
+ /** REG_BINARY
+ *
+ * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine
+ * length.
+ */
+ uint8_t* binary;
+
+ /** REG_DWORD */
+ uint32_t dword;
+
+ /** REG_DWORD_BE */
+ uint32_t dword_be;
+
+ /** REG_LINK
+ *
+ * Stored as a NUL terminated string. Converted to the specified
+ * REGFI_ENCODING.
+ */
+ uint8_t* link;
+
+ /** REG_MULTI_SZ
+ *
+ * Stored as a list of uint8_t* pointers, terminated with a NULL pointer.
+ * Each string element in the list is NUL terminated, and the character set
+ * is determined by the specified REGFI_ENCODING.
+ */
+ uint8_t** multiple_string;
+
+ /** REG_QWORD */
+ uint64_t qword;
+
+ /* The following are treated as binary currently, but this may change in
+ * the future as the formats become better understood.
+ */
+
+ /** REG_RESOURCE_LIST
+ *
+ * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine
+ * length.
+ */
+ uint8_t* resource_list;
+
+ /** REG_FULL_RESOURCE_DESCRIPTOR
+ *
+ * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine
+ * length.
+ */
+ uint8_t* full_resource_descriptor;
+
+ /** REG_RESOURCE_REQUIREMENTS_LIST
+ *
+ * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine
+ * length.
+ */
+ uint8_t* resource_requirements_list;
+ } interpreted;
+} REGFI_DATA;
+
+
+/** Value structure
+ * @ingroup regfiBase
+ */
+typedef struct
+{
+ /** Real offset of this record's cell in the file */
+ uint32_t offset;
+
+ /** ((start_offset - end_offset) & 0xfffffff8) */
+ uint32_t cell_size;
+
+ /* XXX: deprecated */
+ REGFI_DATA* data;
+
+ /** The name of this value converted to desired REGFI_ENCODING.
+ *
+ * This conversion typically occurs automatically through REGFI_ITERATOR
+ * settings. String is NUL terminated.
+ */
+ char* valuename;
+
+ /** The raw value name
+ *
+ * Length of the buffer is stored in name_length.
+ */
+ uint8_t* valuename_raw;
+
+ /** Length of valuename_raw */
+ uint16_t name_length;
+
+ /** Offset from beginning of this hbin block */
+ uint32_t hbin_off;
- uint32 data_size;
- uint32 data_off; /* offset of data cell (virtual) */
- uint32 type;
- uint8 magic[REGFI_CELL_MAGIC_SIZE];
- uint16 flag;
- uint16 unknown1;
- bool data_in_offset;
+ /** Size of the value's data as reported in the VK record.
+ *
+ * May be different than that obtained while parsing the data cell itself.
+ */
+ uint32_t data_size;
+
+ /** Virtual offset of data cell */
+ uint32_t data_off;
+
+ /** Value's data type */
+ uint32_t type;
+
+ /** VK record's magic number (should be "vk") */
+ uint8_t magic[REGFI_CELL_MAGIC_SIZE];
+
+ /** VK record flags */
+ uint16_t flags;
+
+ /* XXX: A 2-byte field of unknown purpose stored in the VK record */
+ uint16_t unknown1;
+
+ /** Whether or not the data record is stored in the VK record's data_off field.
+ *
+ * This information is derived from the high bit of the raw data size field.
+ */
+ bool data_in_offset;
} REGFI_VK_REC;
/* Key Security */
struct _regfi_sk_rec;
+/** Security structure
+ * @ingroup regfiBase
+ */
typedef struct _regfi_sk_rec
{
- uint32 offset; /* Real file offset of this record */
- uint32 cell_size; /* ((start_offset - end_offset) & 0xfffffff8) */
+ /** Real file offset of this record */
+ uint32_t offset;
+ /** ((start_offset - end_offset) & 0xfffffff8) */
+ uint32_t cell_size;
+
+ /** The stored Windows security descriptor for this SK record */
WINSEC_DESC* sec_desc;
- uint32 hbin_off; /* offset from beginning of this hbin block */
+
+ /** Offset of this record from beginning of this hbin block */
+ uint32_t hbin_off;
- uint32 prev_sk_off;
- uint32 next_sk_off;
- uint32 ref_count;
- uint32 desc_size; /* size of security descriptor */
- uint16 unknown_tag;
- uint8 magic[REGFI_CELL_MAGIC_SIZE];
+ /** Offset of the previous SK record in the linked list of SK records */
+ uint32_t prev_sk_off;
+
+ /** Offset of the next SK record in the linked list of SK records */
+ uint32_t next_sk_off;
+
+ /** Number of keys referencing this SK record */
+ uint32_t ref_count;
+
+ /** Size of security descriptor (sec_desc) */
+ uint32_t desc_size;
+
+ /* XXX: A 2-byte field of unknown purpose */
+ uint16_t unknown_tag;
+
+ /** The magic number for this record (should be "sk") */
+ uint8_t magic[REGFI_CELL_MAGIC_SIZE];
} REGFI_SK_REC;
-/* Key Name */
+/** Key structure
+ * @ingroup regfiBase
+ */
typedef struct
{
- uint32 offset; /* Real offset of this record's cell in the file */
- uint32 cell_size; /* Actual or estimated length of the cell.
- * Always in multiples of 8.
- */
+ /** Real offset of this record's cell in the file */
+ uint32_t offset;
+
+ /** Actual or estimated length of the cell.
+ * Always in multiples of 8.
+ */
+ uint32_t cell_size;
- /* link in the other records here */
+ /** Preloaded value-list for this key.
+ * This element is loaded automatically when using the iterator interface and
+ * possibly some lower layer interfaces.
+ */
REGFI_VALUE_LIST* values;
+
+
+ /** Preloaded subkey-list for this key.
+ * This element is loaded automatically when using the iterator interface and
+ * possibly some lower layer interfaces.
+ */
REGFI_SUBKEY_LIST* subkeys;
- /* header information */
- uint16 key_type;
- uint8 magic[REGFI_CELL_MAGIC_SIZE];
- NTTIME mtime;
- uint16 name_length;
- uint16 classname_length;
- char* classname;
+ /** Key flags */
+ uint16_t flags;
+
+ /** Magic number of key (should be "nk") */
+ uint8_t magic[REGFI_CELL_MAGIC_SIZE];
+
+ /** Key's last modification time */
+ REGFI_NTTIME mtime;
+
+ /** Length of keyname_raw */
+ uint16_t name_length;
+
+ /** Length of referenced classname */
+ uint16_t classname_length;
+
+ /** The name of this key converted to desired REGFI_ENCODING.
+ *
+ * This conversion typically occurs automatically through REGFI_ITERATOR
+ * settings. String is NUL terminated.
+ */
char* keyname;
- uint32 parent_off; /* pointer to parent key */
- uint32 classname_off;
+
+ /** The raw key name
+ *
+ * Length of the buffer is stored in name_length.
+ */
+ uint8_t* keyname_raw;
+
+ /** Virutal offset of parent key */
+ uint32_t parent_off;
+
+ /** Virutal offset of classname key */
+ uint32_t classname_off;
- /* max lengths */
- uint32 max_bytes_subkeyname; /* max subkey name * 2 */
- uint32 max_bytes_subkeyclassname; /* max subkey classname length (as if) */
- uint32 max_bytes_valuename; /* max valuename * 2 */
- uint32 max_bytes_value; /* max value data size */
+ /* XXX: max subkey name * 2 */
+ uint32_t max_bytes_subkeyname;
+
+ /* XXX: max subkey classname length (as if) */
+ uint32_t max_bytes_subkeyclassname;
+
+ /* XXX: max valuename * 2 */
+ uint32_t max_bytes_valuename;
+
+ /* XXX: max value data size */
+ uint32_t max_bytes_value;
- /* unknowns */
- uint32 unknown1;
- uint32 unknown2;
- uint32 unknown3;
- uint32 unk_index; /* nigel says run time index ? */
+ /* XXX: Fields of unknown purpose */
+ uint32_t unknown1;
+ uint32_t unknown2;
+ uint32_t unknown3;
+ uint32_t unk_index; /* nigel says run time index ? */
- /* children */
- uint32 num_subkeys;
- uint32 subkeys_off; /* offset of subkey list that points to NK records */
- uint32 num_values;
- uint32 values_off; /* value lists which point to VK records */
- uint32 sk_off; /* offset to SK record */
+ /** Number of subkeys */
+ uint32_t num_subkeys;
+
+ /** Virtual offset of subkey-list */
+ uint32_t subkeys_off;
+
+ /** Number of values for this key */
+ uint32_t num_values;
+
+ /** Virtual offset of value-list */
+ uint32_t values_off;
+
+ /** Virtual offset of SK record */
+ uint32_t sk_off;
} REGFI_NK_REC;
-/* REGF block */
+/** Registry hive file data structure
+ *
+ * This essential structure stores run-time information about a single open
+ * registry hive as well as file header (REGF block) data. This structure
+ * also stores a list of warnings and error messages generated while parsing
+ * the registry hive. These can be tuned using @ref regfi_set_message_mask.
+ * Messages may be retrieved using @ref regfi_get_messages.
+ *
+ * @note If the message mask is set to record any messages, dependent code
+ * must use @ref regfi_get_messages periodically to clear the message
+ * queue. Otherwise, this structure will grow in size over time as
+ * messages queue up.
+ *
+ * @ingroup regfiBase
+ */
typedef struct
{
/* Run-time information */
@@ -343,7 +672,7 @@ typedef struct
int fd;
/* For sanity checking (not part of the registry header) */
- uint32 file_length;
+ uint32_t file_length;
/* Metadata about hbins */
range_list* hbins;
@@ -355,219 +684,732 @@ typedef struct
char* last_message;
/* Mask for error message types that will be stored. */
- uint16 msg_mask;
+ uint16_t msg_mask;
/* Data parsed from file header */
/********************************/
- uint8 magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
+ uint8_t magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
/* These sequence numbers should match if
* the hive was properly synced to disk.
*/
- uint32 sequence1;
- uint32 sequence2;
+ uint32_t sequence1;
+ uint32_t sequence2;
- NTTIME mtime;
- uint32 major_version; /* Set to 1 in all known hives */
- uint32 minor_version; /* Set to 3 or 5 in all known hives */
- uint32 type; /* XXX: Unverified. Set to 0 in all known hives */
- uint32 format; /* XXX: Unverified. Set to 1 in all known hives */
+ REGFI_NTTIME mtime;
+ uint32_t major_version; /* Set to 1 in all known hives */
+ uint32_t minor_version; /* Set to 3 or 5 in all known hives */
+ uint32_t type; /* XXX: Unverified. Set to 0 in all known hives */
+ uint32_t format; /* XXX: Unverified. Set to 1 in all known hives */
- uint32 root_cell; /* Offset to root cell in the first (or any?) hbin block */
- uint32 last_block; /* Offset to last hbin block in file */
+ uint32_t root_cell; /* Offset to root cell in the first (or any?) hbin block */
+ uint32_t last_block; /* Offset to last hbin block in file */
- uint32 cluster; /* XXX: Unverified. Set to 1 in all known hives */
+ uint32_t cluster; /* XXX: Unverified. Set to 1 in all known hives */
/* Matches hive's base file name. Stored in UTF-16LE */
- uint8 file_name[REGFI_REGF_NAME_SIZE];
+ uint8_t file_name[REGFI_REGF_NAME_SIZE];
WINSEC_UUID* rm_id; /* XXX: Unverified. */
WINSEC_UUID* log_id; /* XXX: Unverified. */
WINSEC_UUID* tm_id; /* XXX: Unverified. */
- uint32 flags; /* XXX: Unverified. */
- uint32 guid_signature; /* XXX: Unverified. */
+ uint32_t flags; /* XXX: Unverified. */
+ uint32_t guid_signature; /* XXX: Unverified. */
- uint32 checksum; /* Stored checksum from file */
- uint32 computed_checksum; /* Our own calculation of the checksum.
+ uint32_t checksum; /* Stored checksum from file */
+ uint32_t computed_checksum; /* Our own calculation of the checksum.
* (XOR of bytes 0x0000 - 0x01FB) */
WINSEC_UUID* thaw_tm_id; /* XXX: Unverified. */
WINSEC_UUID* thaw_rm_id; /* XXX: Unverified. */
WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */
- uint32 boot_type; /* XXX: Unverified. */
- uint32 boot_recover; /* XXX: Unverified. */
+ uint32_t boot_type; /* XXX: Unverified. */
+ uint32_t boot_recover; /* XXX: Unverified. */
/* This seems to include random junk. Possibly unsanitized memory left over
* from when header block was written. For instance, chunks of nk records
* can be found, though often it's all 0s. */
- uint8 reserved1[REGFI_REGF_RESERVED1_SIZE];
+ uint8_t reserved1[REGFI_REGF_RESERVED1_SIZE];
/* This is likely reserved and unusued currently. (Should be all 0s.)
* Included here for easier access in looking for hidden data
* or doing research. */
- uint8 reserved2[REGFI_REGF_RESERVED2_SIZE];
+ uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE];
} REGFI_FILE;
-/* XXX: Should move all caching (SK records, HBINs, NKs, etc) to a single
- * structure, probably REGFI_FILE. Once key caching is in place,
- * convert key_positions stack to store just key offsets rather than
- * whole keys.
+/** Registry hive iterator
+ * @ingroup regfiIteratorLayer
*/
typedef struct _regfi_iterator
{
+ /** The registry hive this iterator is associated with */
REGFI_FILE* f;
+
+ /** All current parent keys and associated iterator positions */
void_stack* key_positions;
+
+ /** The current key */
REGFI_NK_REC* cur_key;
- uint32 cur_subkey;
- uint32 cur_value;
+
+ /** The encoding that all strings are converted to as set during iterator
+ * creation.
+ */
+ REGFI_ENCODING string_encoding;
+
+ /** Index of the current subkey */
+ uint32_t cur_subkey;
+
+ /** Index of the current value */
+ uint32_t cur_value;
} REGFI_ITERATOR;
typedef struct _regfi_iter_position
{
REGFI_NK_REC* nk;
- uint32 cur_subkey;
+ uint32_t cur_subkey;
/* We could store a cur_value here as well, but didn't see
* the use in it right now.
*/
} REGFI_ITER_POSITION;
+/** General purpose buffer with stored length
+ * @ingroup regfiBottomLayer
+ */
typedef struct _regfi_buffer
{
- uint8* buf;
+ uint8_t* buf;
uint32_t len;
} REGFI_BUFFER;
+
/******************************************************************************/
-/* Main iterator API */
+/**
+ * @defgroup regfiBase Base Layer: Essential Functions and Data Structures
+ *
+ * These functions are either necessary for normal use of the regfi API or just
+ * don't fit particularly well in any of the other layers.
+ */
/******************************************************************************/
+
+/** Attempts to open a registry hive and allocate related data structures.
+ *
+ * @param filename A string containing the relative or absolute path of the
+ * registry hive to be opened.
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure,
+ * if successful; NULL on error.
+ *
+ * @ingroup regfiBase
+ */
REGFI_FILE* regfi_open(const char* filename);
-int regfi_close(REGFI_FILE* r);
-/* regfi_get_messages: Get errors, warnings, and/or verbose information
- * relating to processing of the given registry file.
+
+/** Parses file headers of an already open registry hive file and
+ * allocates related structures for further parsing.
+ *
+ * @param fd A file descriptor of an already open file. Must be seekable.
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ * NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+REGFI_FILE* regfi_alloc(int fd);
+
+
+/** Closes and frees an open registry hive.
+ *
+ * @param file The registry structure to close.
+ *
+ * @return 0 on success, -1 on failure with errno set.
+ * errno codes are similar to those of close(2).
+ *
+ * @ingroup regfiBase
+ */
+int regfi_close(REGFI_FILE* file);
+
+
+/** Frees a hive's data structures without closing the underlying file.
+ *
+ * @param file The registry structure to free.
+ *
+ * @ingroup regfiBase
+ */
+void regfi_free(REGFI_FILE* file);
+
+
+/** Get errors, warnings, and/or verbose information relating to processing of
+ * the given registry file.
*
- * Arguments:
- * file -- the structure for the registry file
+ * @param file the structure for the registry file
*
- * Returns:
- * A newly allocated char* which must be free()d by the caller.
+ * @return A newly allocated char* which must be free()d by the caller.
+ *
+ * @ingroup regfiBase
*/
char* regfi_get_messages(REGFI_FILE* file);
-void regfi_set_message_mask(REGFI_FILE* file, uint16 mask);
-REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh);
+
+/** Set the verbosity level of errors and warnings generated by the library
+ * (as accessible via regfi_get_messages).
+ *
+ * This may be called at any time and will take effect immediately.
+ *
+ * @param file the structure for the registry file
+ *
+ * @param mask an integer representing the types of messages desired.
+ * Acceptable values are created through bitwise ORs of
+ * REGFI_MSG_* values. For instance, if only errors and
+ * informational messages were desired (but not warnings),
+ * then one would specify: REGFI_MSG_ERROR|REGFI_MSG_INFO
+ * New REGFI_FILE structures are created with:
+ * REGFI_MSG_ERROR|REGFI_MSG_WARN
+ * Note that error and warning messages will continue to
+ * accumulate in memory if they are not fetched using
+ * regfi_get_messages and then freed by the caller.
+ * To disable error messages entirely, supply 0, which
+ * will prevent message accumulation.
+ *
+ * @ingroup regfiBase
+ */
+void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask);
+
+
+/* Dispose of previously parsed records */
+
+/** Frees a key structure previously returned by one of the API functions
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiBase
+ */
+void regfi_free_key(REGFI_NK_REC* nk);
+
+
+/** Frees a value structure previously returned by one of the API functions
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiBase
+ */
+void regfi_free_value(REGFI_VK_REC* vk);
+
+
+
+/******************************************************************************/
+/**
+ * @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface
+ *
+ * This top layer of API functions provides an iterator interface which makes
+ * traversing registry data structures easy in both single-threaded and
+ * multi-threaded scenarios.
+ */
+/******************************************************************************/
+
+/** Creates a new iterator for the provided registry file.
+ *
+ * @param file The opened registry file the iterator should be created for.
+ *
+ * @param output_encoding Character encoding that strings should be returned in.
+ * Only supply the REGFI_ENCODING_* constants, as others
+ * will be rejected.
+ * The following values are currently accepted:
+ * REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ * REGFI_ENCODING_ASCII
+ * REGFI_ENCODING_UTF8
+ *
+ * @return A newly allocated REGFI_ITERATOR.
+ * Must be free()d with regfi_iterator_free.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
+ REGFI_ENCODING output_encoding);
+
+
+/** Frees a registry file iterator previously created by regfi_iterator_new.
+ *
+ * This does not affect the underlying registry file's allocation status.
+ *
+ * @param i the iterator to be freed
+ *
+ * @ingroup regfiIteratorLayer
+ */
void regfi_iterator_free(REGFI_ITERATOR* i);
+
+
+/** Traverse deeper into the registry tree at the current subkey.
+ *
+ * @param i the iterator
+ *
+ * @return true on success, false on failure.
+ * Note that subkey and value indexes are preserved. That is, if a
+ * regfi_iterator_up call occurs later (reversing the effect of this
+ * call) then the subkey and value referenced prior to the
+ * regfi_iterator_down call will still be referenced. This makes
+ * depth-first iteration particularly easy.
+ *
+ * @ingroup regfiIteratorLayer
+ */
bool regfi_iterator_down(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the current key's parent key.
+ *
+ * @param i the iterator
+ *
+ * @return true on success, false on failure. Any subkey or value state
+ * associated with the current key is lost.
+ *
+ * @ingroup regfiIteratorLayer
+ */
bool regfi_iterator_up(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the root key of the hive.
+ *
+ * @param i the iterator
+ *
+ * @return true on success, false on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
bool regfi_iterator_to_root(REGFI_ITERATOR* i);
-bool regfi_iterator_find_subkey(REGFI_ITERATOR* i,
- const char* subkey_name);
-bool regfi_iterator_walk_path(REGFI_ITERATOR* i,
- const char** path);
+
+/** Traverse down multiple levels in the registry hive.
+ *
+ * XXX: This currently only accepts ASCII key names. Need to look into
+ * accepting other encodings.
+ *
+ * @param i the iterator
+ * @param path a list of key names representing the path. This list must
+ * contain NUL terminated strings. The list itself is
+ * terminated with a NULL pointer. All path elements must be
+ * keys; value names are not accepted (even as the last
+ * element).
+ *
+ * @return true on success, false on failure. If any element of path is not
+ * found, false will be returned and the iterator will remain
+ * in its original position.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
+
+
+/** Returns the currently referenced key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only key structure for the current key, or NULL on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i);
+
+
+/** Returns the SK (security) record referenced by the current key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+
+/** Sets the internal subkey index to the first subkey referenced by the current
+ * key and returns that key.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the newly referenced first
+ * subkey, or NULL on failure. Failure may be due to a lack of any
+ * subkeys or other errors. Newly allocated keys must be freed with
+ * regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed subkey.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the currently referenced subkey,
+ * or NULL on failure. Newly allocated keys must be freed with
+ * regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+
+
+/** Increments the internal subkey index to the next key in the subkey-list and
+ * returns the subkey for that index.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the next subkey or NULL on
+ * failure. Newly allocated keys must be freed with regfi_free_key.
+ *
+ * @ingroup regfiIteratorLayer
+ */
REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i);
-bool regfi_iterator_find_value(REGFI_ITERATOR* i,
- const char* value_name);
+
+/** Searches for a subkey with a given name under the current key.
+ *
+ * @param i the iterator
+ * @param subkey_name subkey name to search for
+ *
+ * @return True if such a subkey was found, false otherwise. If a subkey is
+ * found, the current subkey index is set to that subkey. Otherwise,
+ * the subkey index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i,
+ const char* subkey_name);
+
+/** Sets the internal value index to the first value referenced by the current
+ * key and returns that value.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated value structure for the newly referenced first
+ * value, or NULL on failure. Failure may be due to a lack of any
+ * values or other errors. Newly allocated keys must be freed with
+ * regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed value.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated value structure for the currently referenced value,
+ * or NULL on failure. Newly allocated values must be freed with
+ * regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i);
+
+
+/** Increments the internal value index to the next value in the value-list and
+ * returns the value for that index.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the next value or NULL on
+ * failure. Newly allocated keys must be freed with regfi_free_value.
+ *
+ * @ingroup regfiIteratorLayer
+ */
REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i);
-/********************************************************/
-/* Middle-layer structure loading, linking, and caching */
-/********************************************************/
-REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset,
+/** Searches for a value with a given name under the current key.
+ *
+ * @param i the iterator
+ * @param value_name value name to search for
+ *
+ * @return True if such a value was found, false otherwise. If a value is
+ * found, the current value index is set to that value. Otherwise,
+ * the value index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+bool regfi_iterator_find_value(REGFI_ITERATOR* i,
+ const char* value_name);
+
+/** Retrieves classname for a given key.
+ *
+ * @param i the iterator
+ * @param key the key whose classname is desired
+ *
+ * @return Returns a newly allocated classname structure, or NULL on failure.
+ * Classname structures must be freed with regfi_free_classname.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
+ const REGFI_NK_REC* key);
+
+
+/** Retrieves data for a given value.
+ *
+ * @param i the iterator
+ * @param value the value whose data is desired
+ *
+ * @return Returns a newly allocated data structure, or NULL on failure.
+ * Data structures must be freed with regfi_free_data.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
+ const REGFI_VK_REC* value);
+
+
+
+/******************************************************************************/
+/**
+ * @defgroup regfiGlueLayer Glue Layer: Logical Data Structure Loading
+ */
+/******************************************************************************/
+
+/** Loads a key at a given file offset along with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
+ REGFI_ENCODING output_encoding,
bool strict);
-REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset,
+
+
+/** Loads a value at a given file offset alng with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
+ REGFI_ENCODING output_encoding,
bool strict);
-REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
- uint32 num_keys, uint32 max_size,
+
+
+/** Loads a logical subkey list in its entirety which may span multiple records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_keys, uint32_t max_size,
bool strict);
-REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
- uint32 num_values, uint32 max_size,
+
+
+/** Loads a valuelist.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_values, uint32_t max_size,
bool strict);
+
+/** Loads a data record which may be contained in the virtual offset, in a
+ * single cell, or in multiple cells through big data records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+ uint32_t length, bool data_in_offset,
+ bool strict);
+
+
+/** Loads the data associated with a big data record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset,
+ uint32_t data_length,uint32_t cell_length,
+ range_list* used_ranges,
+ bool strict);
+
+
+/** Given raw data, attempts to interpret the data based on a specified registry
+ * data type.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+bool regfi_interpret_data(REGFI_FILE* file,
+ REGFI_ENCODING string_encoding,
+ uint32_t type, REGFI_DATA* data);
+
+
+/** Frees the memory associated with a REGFI_CLASSNAME data structure.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+void regfi_free_classname(REGFI_CLASSNAME* classname);
+
+
+/** Frees the memory associated with a REGFI_DATA data structure.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+void regfi_free_data(REGFI_DATA* data);
+
+
/* These are cached so return values don't need to be freed. */
-const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset,
+
+/** Loads an "sk" security record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
bool strict);
-const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset);
+/** Retrieves the HBIN data structure stored at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
+
+
+
+/******************************************************************************/
+/**
+ * @defgroup regfiParseLayer Parsing Layer: Direct Data Structure Access
+ */
+/******************************************************************************/
-/************************************/
-/* Low-layer data structure access */
-/************************************/
REGFI_FILE* regfi_parse_regf(int fd, bool strict);
-REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset,
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset,
bool strict);
-/* regfi_parse_nk: Parses an NK record.
+/** Parses an NK record at the specified offset
+ *
+ * @param file the registry file structure
+ * @param offset the offset of the cell (not the record) to be parsed.
+ * @param max_size the maximum size the NK cell could be. (for validation)
+ * @param strict if true, rejects any malformed records. Otherwise,
+ * tries to minimally validate integrity.
+ *
+ * @return A newly allocated NK record structure, or NULL on failure.
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
+
+
+/** Parses a single cell containing a subkey-list record.
*
- * Arguments:
- * f -- the registry file structure
- * offset -- the offset of the cell (not the record) to be parsed.
- * max_size -- the maximum size the NK cell could be. (for validation)
- * strict -- if true, rejects any malformed records. Otherwise,
- * tries to minimally validate integrity.
- * Returns:
- * A newly allocated NK record structure, or NULL on failure.
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
*/
-REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict);
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
-REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict);
-REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict);
+/** Parses a VK (value) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
-REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
- uint32 data_type, uint32 offset,
- uint32 length, uint32 max_size,
- bool data_in_offset, bool strict);
-REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
- uint32 offset, uint32 data_length,
- uint32 cell_length, bool strict);
+/** Parses an SK (security) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict);
-REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict);
+/** Retrieves information on all cells in the registry hive which are
+ * currently in the unallocated status.
+ *
+ * The unallocated status is determined based soley on the cell length sign.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
-bool regfi_parse_cell(int fd, uint32 offset,
- uint8* hdr, uint32 hdr_len,
- uint32* cell_length, bool* unalloc);
-char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
- uint16* name_length,
- uint32 max_size, bool strict);
+/** Helper function to parse a cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+bool regfi_parse_cell(int fd, uint32_t offset,
+ uint8_t* hdr, uint32_t hdr_len,
+ uint32_t* cell_length, bool* unalloc);
+
+
+/** Parses a classname cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+ uint16_t* name_length,
+ uint32_t max_size, bool strict);
+
+
+/** Parses a single-cell data record
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+ uint32_t length, bool strict);
-/* Dispose of previously parsed records */
-void regfi_free_key(REGFI_NK_REC* nk);
-void regfi_free_value(REGFI_VK_REC* vk);
+/** Parses a "little data" record which is stored entirely within the
+ * provided virtual offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
+ uint32_t length, bool strict);
-/************************************/
-/* Private Functions */
-/************************************/
-REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file);
+/******************************************************************************/
+/* Private Functions */
+/******************************************************************************/
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file,
+ REGFI_ENCODING output_encoding);
void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
-uint32 regfi_read(int fd, uint8* buf, uint32* length);
+uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length);
const char* regfi_type_val2str(unsigned int val);
int regfi_type_str2val(const char* str);
@@ -577,15 +1419,32 @@ char* regfi_get_dacl(WINSEC_DESC* sec_desc);
char* regfi_get_owner(WINSEC_DESC* sec_desc);
char* regfi_get_group(WINSEC_DESC* sec_desc);
-REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists,
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
REGFI_SUBKEY_LIST** lists,
bool strict);
-REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict,
- uint8 depth_left);
-void regfi_add_message(REGFI_FILE* file, uint16 msg_type,
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict,
+ uint8_t depth_left);
+void regfi_add_message(REGFI_FILE* file, uint16_t msg_type,
const char* fmt, ...);
REGFI_NK_REC* regfi_copy_nk(const REGFI_NK_REC* nk);
REGFI_VK_REC* regfi_copy_vk(const REGFI_VK_REC* vk);
+int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset);
+int32_t regfi_conv_charset(const char* input_charset,
+ const char* output_charset,
+ uint8_t* input, char* output,
+ uint32_t input_len, uint32_t output_max);
+REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data);
+
+/* XXX: move to base API and document */
+void regfi_unix2nt_time(REGFI_NTTIME* nt, time_t t);
+time_t regfi_nt2unix_time(const REGFI_NTTIME* nt);
+
+
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
+ REGFI_ENCODING output_encoding, bool strict);
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
+ REGFI_ENCODING output_encoding, bool strict);
+
#endif /* _REGFI_H */
diff --git a/include/smb_deps.h b/include/smb_deps.h
deleted file mode 100644
index cae3f8b..0000000
--- a/include/smb_deps.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * This file contains miscellaneous pieces of code which regfio.c
- * depends upon, from the Samba Subversion tree. See:
- * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
- *
- * Copyright (C) 2005,2009 Timothy D. Morgan
- * Copyright (C) 1992-2005 Samba development team
- * (see individual files under Subversion for details.)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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.
- *
- * $Id: smb_deps.h 134 2009-01-16 18:36:04Z tim $
- */
-
-#ifndef _SMB_DEPS_H
-#define _SMB_DEPS_H
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "byteorder.h"
-
-#define DEBUG(lvl,body) 0
-
-void* zalloc(size_t size);
-void* zcalloc(size_t size, unsigned int count);
-
-/* From includes.h */
-
-#define uint8 uint8_t
-#define int16 int8_t
-#define uint16 uint16_t
-#define int32 int32_t
-#define uint32 uint32_t
-
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#define MAX(a,b) ((a)>(b)?(a):(b))
-
-extern int DEBUGLEVEL;
-
-/* End of stuff from includes.h */
-
-/* From smb.h */
-
-typedef struct nttime_info
-{
- uint32 low;
- uint32 high;
-} NTTIME;
-
-/* End of stuff from smb.h */
-
-/* From lib/time.c */
-
-#define CHAR_BIT 8
-#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
- : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
-#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
-#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
-
-void unix_to_nt_time(NTTIME* nt, time_t t);
-time_t nt_time_to_unix(const NTTIME* nt);
-
-/* End of stuff from lib/time.c */
-
-#endif /* _SMB_DEPS_H */
diff --git a/include/talloc.h b/include/talloc.h
index f34065c..d7a08a6 100644
--- a/include/talloc.h
+++ b/include/talloc.h
@@ -22,7 +22,7 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-/* $Id: talloc.h 147 2009-02-22 19:31:52Z tim $ */
+/* $Id: talloc.h 168 2010-03-03 00:08:42Z tim $ */
#ifndef _TALLOC_H_
#define _TALLOC_H_
@@ -32,7 +32,14 @@
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
-#include "smb_deps.h" /* MAX macro */
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
/*
this uses a little trick to allow __LINE__ to be stringified
diff --git a/include/void_stack.h b/include/void_stack.h
index 42fefb6..dc1c048 100644
--- a/include/void_stack.h
+++ b/include/void_stack.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005,2007,2009 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
*
* 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
@@ -14,9 +14,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: void_stack.h 150 2009-03-02 02:17:46Z tim $
+ * $Id: void_stack.h 169 2010-03-03 19:24:58Z tim $
*/
+/**
+ *@file
+ *
+ * This is a very simple implementation of a stack which stores chunks of
+ * memory of any type.
+ */
+
+
#ifndef _VOID_STACK_H
#define _VOID_STACK_H
@@ -25,6 +33,7 @@
#include <string.h>
#include "talloc.h"
+/** XXX: document this. */
typedef struct _void_stack
{
void** elements;
@@ -32,6 +41,8 @@ typedef struct _void_stack
unsigned short top;
} void_stack;
+
+/** XXX: document this. */
typedef struct _void_stack_iterator
{
const void_stack* stack;
@@ -39,149 +50,120 @@ typedef struct _void_stack_iterator
} void_stack_iterator;
-/* void_stack_new():
- * Allocates a new void_stack.
+/** Allocates a new void_stack.
*
- * Arguments:
- * max_size -- the maxiumum number of elements
- * which may be pushed onto the stack.
+ * @param max_size the maxiumum number of elements
+ * which may be pushed onto the stack.
*
- * Returns:
- * a pointer to the newly allocated void_stack, or NULL if an error occurred.
+ * @return a pointer to the newly allocated void_stack,
+ * or NULL if an error occurred.
*/
void_stack* void_stack_new(unsigned short max_size);
-/* void_stack_copy():
- * Makes a shallow copy of void_stack.
+/** Makes a shallow copy of void_stack.
*
- * Arguments:
- * v -- the stack to make a copy of.
+ * @param v the stack to make a copy of.
*
- * Returns:
- * a pointer to the duplicate void_stack, or NULL If an error occurred.
+ * @return a pointer to the duplicate void_stack, or NULL if an error occurred.
*/
void_stack* void_stack_copy(const void_stack* v);
-/* void_stack_copy_reverse():
- * Makes a shallow copy of void_stack in reverse order.
+/** Makes a shallow copy of void_stack in reverse order.
*
- * Arguments:
- * v -- the stack to make a copy of.
+ * @param v the stack to make a copy of.
*
- * Returns:
- * a pointer to the duplicate void_stack (which will be in reverse order),
- * or NULL If an error occurred.
+ * @return a pointer to the duplicate void_stack
+ * (which will be in reverse order), or NULL if an error occurred.
*/
void_stack* void_stack_copy_reverse(const void_stack* v);
-/* void_stack_free():
- * Frees the memory associated with a void_stack, but not the elements held
+/** Frees the memory associated with a void_stack, but not the elements held
* on the stack.
*
- * Arguments:
- * stack -- the stack to be free()d.
+ * @param stack the stack to be free()d.
*/
void void_stack_free(void_stack* stack);
-/* void_stack_free_deep():
- * Frees the memory associated with a void_stack and the elements referenced
- * by the stack. Do not use this function if the elements of the stack
- * are also free()d elsewhere, or contain pointers to other memory which
- * cannot be otherwise free()d.
+/** Frees the memory associated with a void_stack and the elements referenced
+ * by the stack.
*
- * Arguments:
- * stack -- the stack to be free()d.
+ * Do not use this function if the elements of the stack
+ * are also free()d elsewhere, or contain pointers to other memory which
+ * cannot be otherwise free()d.
+ *
+ * @param stack the stack to be free()d.
*/
void void_stack_free_deep(void_stack* stack);
-/* void_stack_size():
- * Query the current number of elements on a void_stack()
+/** Query the current number of elements on a void_stack()
*
- * Arguments:
- * stack -- the void_stack to query
+ * @param stack the void_stack to query
*
- * Returns:
- * the number of elements currently on the stack.
+ * @return the number of elements currently on the stack.
*/
unsigned short void_stack_size(const void_stack* stack);
-/* void_stack_pop():
- * Removes the top element on a void_stack and returns a reference to it.
+/** Removes the top element on a void_stack and returns a reference to it.
*
- * Arguments:
- * stack -- the void_stack to pop
+ * @param stack the void_stack to pop
*
- * Returns:
- * a pointer to the popped stack element, or NULL if no elements exist on
- * the stack.
+ * @return a pointer to the popped stack element, or NULL if no elements exist
+ * on the stack.
*/
void* void_stack_pop(void_stack* stack);
-/* void_stack_push():
- * Puts a new element on the top of a void_stack.
- *
- * Arguments:
- * stack -- the void_stack being modified.
+/** Puts a new element on the top of a void_stack.
*
- * e -- the element to be added
+ * @param stack the void_stack being modified.
+ * @param e the element to be added
*
- * Returns:
- * true if the element was successfully added, false otherwise.
+ * @return true if the element was successfully added, false otherwise.
*/
bool void_stack_push(void_stack* stack, void* e);
-/* void_stack_cur():
- * Returns a pointer to the current element on the top of the stack.
+/** Returns a pointer to the current element on the top of the stack.
*
- * Arguments:
- * stack -- the void_stack being queried.
+ * @param stack the void_stack being queried.
*
- * Returns:
- * a pointer to the current element on the top of the stack, or NULL if no
- * elements exist in the stack.
+ * @return a pointer to the current element on the top of the stack, or NULL if
+ * no elements exist in the stack.
*/
const void* void_stack_cur(const void_stack* stack);
-/* void_stack_iterator_new():
- * Creates a new iterator for the specified void_stack.
+/** Creates a new iterator for the specified void_stack.
*
- * Arguments:
- * stack -- the void_stack to be referenced by the new iterator
+ * @param stack the void_stack to be referenced by the new iterator
*
- * Returns:
- * a new void_stack_iterator, or NULL if an error occurred.
+ * @return a new void_stack_iterator, or NULL if an error occurred.
*/
void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
-/* void_stack_iterator_free():
- * Frees a void_stack_iterator. Does not affect the void_stack referenced
- * by the iterator.
+/** Frees a void_stack_iterator.
*
- * Arguments:
- * iter -- the void_stack_iterator to be free()d.
+ * Does not affect the void_stack referenced by the iterator.
+ *
+ * @param iter the void_stack_iterator to be free()d.
*/
void void_stack_iterator_free(void_stack_iterator* iter);
-/* void_stack_iterator_next():
- * Returns a pointer to the the next element in the stack. Iterates over
- * elements starting in order from the oldest element (bottom of the stack).
+/** Returns a pointer to the the next element in the stack.
+ *
+ * Iterates over elements starting in order from the oldest element (bottom of the stack).
*
- * Arguments:
- * iter -- the void_stack_iterator used to lookup the next element.
+ * @param iter the void_stack_iterator used to lookup the next element.
*
- * Returns:
- * a pointer to the next element.
+ * @return a pointer to the next element.
*/
const void* void_stack_iterator_next(void_stack_iterator* iter);
diff --git a/include/winsec.h b/include/winsec.h
index 1c2db4a..6c7de49 100644
--- a/include/winsec.h
+++ b/include/winsec.h
@@ -1,13 +1,5 @@
-/*
- * This file contains refactored Samba code used to interpret Windows
- * Security Descriptors. See:
- * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
- *
- * Revisions have been made based on information provided by Microsoft
- * at:
- * http://msdn.microsoft.com/en-us/library/cc230366(PROT.10).aspx
- *
- * Copyright (C) 2005,2009 Timothy D. Morgan
+/*
+ * Copyright (C) 2005,2009-2010 Timothy D. Morgan
* Copyright (C) 1992-2005 Samba development team
*
* This program is free software; you can redistribute it and/or modify
@@ -23,7 +15,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: winsec.h 148 2009-02-22 23:22:59Z tim $
+ * $Id: winsec.h 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
+ *
+ * A small library for interpreting Windows Security Descriptors.
+ * This library was originally based on Samba source from:
+ * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * The library has been heavily rewritten and improved based on information
+ * provided by Microsoft at:
+ * http://msdn.microsoft.com/en-us/library/cc230366%28PROT.10%29.aspx
*/
#ifndef _WINSEC_H
@@ -40,8 +44,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include "smb_deps.h"
#include "talloc.h"
+#include "byteorder.h"
/* This is the maximum number of subauths in a SID, as defined here:
@@ -69,103 +73,240 @@
#define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8
+/** XXX: document this. */
typedef struct _winsec_uuid
{
- uint32 time_low;
- uint16 time_mid;
- uint16 time_hi_and_version;
- uint8 clock_seq[2];
- uint8 node[6];
+ /** XXX: document this. */
+ uint32_t time_low;
+
+ /** XXX: document this. */
+ uint16_t time_mid;
+
+ /** XXX: document this. */
+ uint16_t time_hi_and_version;
+
+ /** XXX: document this. */
+ uint8_t clock_seq[2];
+
+ /** XXX: document this. */
+ uint8_t node[6];
} WINSEC_UUID;
+/** XXX: document this. */
typedef struct _winsec_sid
{
- uint8_t sid_rev_num; /* SID revision number */
- uint8_t num_auths; /* Number of sub-authorities */
- uint8_t id_auth[6]; /* Identifier Authority */
- /*
- * Pointer to sub-authorities.
- *
+ /** SID revision number */
+ uint8_t sid_rev_num;
+
+ /** Number of sub-authorities */
+ uint8_t num_auths;
+
+ /** Identifier Authority */
+ uint8_t id_auth[6];
+
+ /** Pointer to sub-authorities.
+ *
* @note The values in these uint32_t's are in *native* byteorder, not
* neccessarily little-endian...... JRA.
*/
- /* XXX: Make this dynamically allocated? */
- uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];
+ uint32_t sub_auths[WINSEC_MAX_SUBAUTHS]; /* XXX: Make this dynamically allocated? */
} WINSEC_DOM_SID;
+/** XXX: document this. */
typedef struct _winsec_ace
{
- uint8_t type; /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
- uint8_t flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
- uint16_t size;
- uint32_t access_mask;
+ /** xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+ uint8_t type;
+
+ /** xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+ uint8_t flags;
+
+ /** XXX: finish documenting */
+ uint16_t size;
+
+ /** XXX: finish documenting */
+ uint32_t access_mask;
+
+ /* This stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+
+ /** xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+ uint32_t obj_flags;
- /* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
- uint32_t obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
- WINSEC_UUID* obj_guid; /* object GUID */
- WINSEC_UUID* inh_guid; /* inherited object GUID */
- /* eof object stuff */
+ /** Object GUID */
+ WINSEC_UUID* obj_guid;
- WINSEC_DOM_SID* trustee;
+ /** Inherited object GUID */
+ WINSEC_UUID* inh_guid;
+
+ /* eof object stuff */
+
+ /** XXX: finish documenting */
+ WINSEC_DOM_SID* trustee;
} WINSEC_ACE;
+
+/** XXX: document this. */
typedef struct _winsec_acl
{
- uint16_t revision; /* 0x0003 */
- uint16_t size; /* size in bytes of the entire ACL structure */
- uint32_t num_aces; /* number of Access Control Entries */
+ /** 0x0003 */
+ uint16_t revision;
+
+ /** Size, in bytes, of the entire ACL structure */
+ uint16_t size;
- WINSEC_ACE** aces;
+ /** Number of Access Control Entries */
+ uint32_t num_aces;
+
+ /** XXX: document this. */
+ WINSEC_ACE** aces;
} WINSEC_ACL;
+
+/** XXX: document this. */
typedef struct _winsec_desc
{
- uint8_t revision; /* 0x01 */
- uint8_t sbz1; /* "If the Control field has the RM flag set,
- * then this field contains the resource
- * manager (RM) control value. ... Otherwise,
- * this field is reserved and MUST be set to
- * zero." -- Microsoft. See reference above.
- */
- uint16_t control; /* WINSEC_DESC_* flags */
-
- uint32_t off_owner_sid; /* offset to owner sid */
- uint32_t off_grp_sid ; /* offset to group sid */
- uint32_t off_sacl ; /* offset to system list of permissions */
- uint32_t off_dacl ; /* offset to list of permissions */
-
- WINSEC_DOM_SID* owner_sid;
- WINSEC_DOM_SID* grp_sid;
- WINSEC_ACL* sacl; /* system ACL */
- WINSEC_ACL* dacl; /* user ACL */
+ /** 0x01 */
+ uint8_t revision;
+
+ /** XXX: better explain this
+ *
+ * "If the Control field has the RM flag set, then this field contains the
+ * resource manager (RM) control value. ... Otherwise, this field is reserved
+ * and MUST be set to zero." -- Microsoft.
+ * See:
+ * http://msdn.microsoft.com/en-us/library/cc230371%28PROT.10%29.aspx
+ */
+ uint8_t sbz1;
+
+ /** WINSEC_DESC_* flags */
+ uint16_t control;
+
+ /** Offset to owner sid */
+ uint32_t off_owner_sid;
+
+ /** Offset to group sid */
+ uint32_t off_grp_sid;
+
+ /** Offset to system list of permissions */
+ uint32_t off_sacl;
+
+ /** Offset to list of permissions */
+ uint32_t off_dacl;
+
+ /** XXX: document this */
+ WINSEC_DOM_SID* owner_sid;
+
+ /** XXX: document this */
+ WINSEC_DOM_SID* grp_sid;
+
+ /** System ACL */
+ WINSEC_ACL* sacl;
+
+ /** User ACL */
+ WINSEC_ACL* dacl;
} WINSEC_DESC;
+
+/**
+ *
+ * XXX: finish documenting
+ */
WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
void winsec_free_descriptor(WINSEC_DESC* desc);
+/**
+ *
+ * XXX: finish documenting
+ */
WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
bool winsec_ace_object(uint8_t type);
#endif /* _WINSEC_H */
diff --git a/lib/Makefile b/lib/Makefile
index c761c61..022ae9b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -2,16 +2,13 @@
################################################################################
-FILES=regfi.o smb_deps.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
+FILES=regfi.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
all: $(FILES)
regfi.o: regfi.c
$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi.c
-smb_deps.o: smb_deps.c
- $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ smb_deps.c
-
winsec.o: winsec.c
$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index 74f5f08..dfa9111 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -14,7 +14,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: lru_cache.c 147 2009-02-22 19:31:52Z tim $
+ * $Id: lru_cache.c 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
*/
#include "lru_cache.h"
diff --git a/lib/range_list.c b/lib/range_list.c
index 7e80a72..80b97f0 100644
--- a/lib/range_list.c
+++ b/lib/range_list.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
*
* 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
@@ -14,7 +14,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: range_list.c 150 2009-03-02 02:17:46Z tim $
+ * $Id: range_list.c 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
*/
#include "range_list.h"
diff --git a/lib/regfi.c b/lib/regfi.c
index 7a6da3c..d292dfe 100644
--- a/lib/regfi.c
+++ b/lib/regfi.c
@@ -1,10 +1,5 @@
/*
- * Branched from Samba project Subversion repository, version #7470:
- * http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
- *
- * Windows NT (and later) registry parsing library
- *
- * Copyright (C) 2005-2009 Timothy D. Morgan
+ * Copyright (C) 2005-2010 Timothy D. Morgan
* Copyright (C) 2005 Gerald (Jerry) Carter
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +13,22 @@
*
* 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.
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: regfi.c 173 2010-03-08 03:39:09Z tim $
+ */
+
+/**
+ * @file
+ *
+ * Windows NT (and later) read-only registry library
+ *
+ * See @ref regfi.h for more information.
*
- * $Id: regfi.c 155 2009-06-03 23:45:58Z tim $
+ * Branched from Samba project Subversion repository, version #7470:
+ * http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
*/
#include "regfi.h"
@@ -32,16 +40,18 @@ static const char* regfi_type_names[] =
{"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
"MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
+const char* regfi_encoding_names[] =
+ {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"};
/******************************************************************************
******************************************************************************/
-void regfi_add_message(REGFI_FILE* file, uint16 msg_type, const char* fmt, ...)
+void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...)
{
/* XXX: This function is not particularly efficient,
* but then it is mostly used during errors.
*/
- uint32 buf_size, buf_used;
+ uint32_t buf_size, buf_used;
char* new_msg;
va_list args;
@@ -95,13 +105,27 @@ char* regfi_get_messages(REGFI_FILE* file)
}
-void regfi_set_message_mask(REGFI_FILE* file, uint16 mask)
+void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask)
{
file->msg_mask = mask;
}
-/* Returns NULL on error */
+/******************************************************************************
+ * Returns NULL for an invalid e
+ *****************************************************************************/
+static const char* regfi_encoding_int2str(REGFI_ENCODING e)
+{
+ if(e < REGFI_NUM_ENCODINGS)
+ return regfi_encoding_names[e];
+
+ return NULL;
+}
+
+
+/******************************************************************************
+ * Returns NULL for an invalid val
+ *****************************************************************************/
const char* regfi_type_val2str(unsigned int val)
{
if(val == REG_KEY)
@@ -114,7 +138,9 @@ const char* regfi_type_val2str(unsigned int val)
}
-/* Returns -1 on error */
+/******************************************************************************
+ * Returns -1 on error
+ *****************************************************************************/
int regfi_type_str2val(const char* str)
{
int i;
@@ -135,7 +161,7 @@ int regfi_type_str2val(const char* str)
/* Security descriptor formatting functions */
-const char* regfi_ace_type2str(uint8 type)
+const char* regfi_ace_type2str(uint8_t type)
{
static const char* map[7]
= {"ALLOW", "DENY", "AUDIT", "ALARM",
@@ -155,7 +181,7 @@ const char* regfi_ace_type2str(uint8 type)
/* For more info, see:
* http://msdn2.microsoft.com/en-us/library/aa772242.aspx
*/
-char* regfi_ace_flags2str(uint8 flags)
+char* regfi_ace_flags2str(uint8_t flags)
{
static const char* flag_map[32] =
{ "OI", /* Object Inherit */
@@ -170,8 +196,8 @@ char* regfi_ace_flags2str(uint8 flags)
char* ret_val = malloc(35*sizeof(char));
char* fo = ret_val;
- uint32 i;
- uint8 f;
+ uint32_t i;
+ uint8_t f;
if(ret_val == NULL)
return NULL;
@@ -204,9 +230,9 @@ char* regfi_ace_flags2str(uint8 flags)
}
-char* regfi_ace_perms2str(uint32 perms)
+char* regfi_ace_perms2str(uint32_t perms)
{
- uint32 i, p;
+ uint32_t i, p;
/* This is more than is needed by a fair margin. */
char* ret_val = malloc(350*sizeof(char));
char* r = ret_val;
@@ -285,9 +311,9 @@ char* regfi_ace_perms2str(uint32 perms)
char* regfi_sid2str(WINSEC_DOM_SID* sid)
{
- uint32 i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
- uint32 left = size;
- uint8 comps = sid->num_auths;
+ uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
+ uint32_t left = size;
+ uint8_t comps = sid->num_auths;
char* ret_val = malloc(size);
if(ret_val == NULL)
@@ -307,7 +333,7 @@ char* regfi_sid2str(WINSEC_DOM_SID* sid)
char* regfi_get_acl(WINSEC_ACL* acl)
{
- uint32 i, extra, size = 0;
+ uint32_t i, extra, size = 0;
const char* type_str;
char* flags_str;
char* perms_str;
@@ -406,10 +432,10 @@ char* regfi_get_group(WINSEC_DESC *sec_desc)
* parameter by reference. If both the return value and length parameter are
* returned as 0, then EOF was encountered immediately
*****************************************************************************/
-uint32 regfi_read(int fd, uint8* buf, uint32* length)
+uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
{
- uint32 rsize = 0;
- uint32 rret = 0;
+ uint32_t rsize = 0;
+ uint32_t rret = 0;
do
{
@@ -430,12 +456,12 @@ uint32 regfi_read(int fd, uint8* buf, uint32* length)
/*****************************************************************************
*
*****************************************************************************/
-bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
- uint32* cell_length, bool* unalloc)
+bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
+ uint32_t* cell_length, bool* unalloc)
{
- uint32 length;
- int32 raw_length;
- uint8 tmp[4];
+ uint32_t length;
+ int32_t raw_length;
+ uint8_t tmp[4];
if(lseek(fd, offset, SEEK_SET) == -1)
return false;
@@ -470,11 +496,11 @@ bool regfi_parse_cell(int fd, uint32 offset, uint8* hdr, uint32 hdr_len,
}
-/*******************************************************************
+/******************************************************************************
* Given an offset and an hbin, is the offset within that hbin?
* The offset is a virtual file offset.
- *******************************************************************/
-static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
+ ******************************************************************************/
+static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32_t voffset)
{
if(!hbin)
return false;
@@ -488,22 +514,36 @@ static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32 voffset)
-/*******************************************************************
- * Provide a virtual offset and receive the correpsonding HBIN
+/******************************************************************************
+ * Provide a physical offset and receive the correpsonding HBIN
* block for it. NULL if one doesn't exist.
- *******************************************************************/
-const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32 voffset)
+ ******************************************************************************/
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset)
{
- return (const REGFI_HBIN*)range_list_find_data(file->hbins,
- voffset+REGFI_REGF_SIZE);
+ return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
}
+/******************************************************************************
+ * Calculate the largest possible cell size given a physical offset.
+ * Largest size is based on the HBIN the offset is currently a member of.
+ * Returns negative values on error.
+ * (Since cells can only be ~2^31 in size, this works out.)
+ ******************************************************************************/
+int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset)
+{
+ const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
+ if(hbin == NULL)
+ return -1;
+
+ return (hbin->block_size + hbin->file_off) - offset;
+}
+
/******************************************************************************
******************************************************************************/
-REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
- uint32 num_keys, uint32 max_size,
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_keys, uint32_t max_size,
bool strict)
{
REGFI_SUBKEY_LIST* ret_val;
@@ -535,14 +575,14 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32 offset,
/******************************************************************************
******************************************************************************/
-REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict,
- uint8 depth_left)
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict,
+ uint8_t depth_left)
{
REGFI_SUBKEY_LIST* ret_val;
REGFI_SUBKEY_LIST** sublists;
- const REGFI_HBIN* sublist_hbin;
- uint32 i, num_sublists, off, max_length;
+ uint32_t i, num_sublists, off;
+ int32_t sublist_maxsize;
if(depth_left == 0)
{
@@ -564,15 +604,13 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
for(i=0; i < num_sublists; i++)
{
off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
- sublist_hbin = regfi_lookup_hbin(file, ret_val->elements[i].offset);
- if(sublist_hbin == NULL)
+
+ sublist_maxsize = regfi_calc_maxsize(file, off);
+ if(sublist_maxsize < 0)
sublists[i] = NULL;
else
- {
- max_length = sublist_hbin->block_size + sublist_hbin->file_off - off;
- sublists[i] = regfi_load_subkeylist_aux(file, off, max_length, strict,
- depth_left-1);
- }
+ sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize,
+ strict, depth_left-1);
}
talloc_free(ret_val);
@@ -585,13 +623,13 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32 offset,
/******************************************************************************
******************************************************************************/
-REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict)
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict)
{
REGFI_SUBKEY_LIST* ret_val;
- uint32 i, cell_length, length, elem_size, read_len;
- uint8* elements = NULL;
- uint8 buf[REGFI_SUBKEY_LIST_MIN_LEN];
+ uint32_t i, cell_length, length, elem_size, read_len;
+ uint8_t* elements = NULL;
+ uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
bool unalloc;
bool recursive_type;
@@ -616,10 +654,10 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
if(buf[0] == 'r' && buf[1] == 'i')
{
recursive_type = true;
- elem_size = sizeof(uint32);
+ elem_size = sizeof(uint32_t);
}
else if(buf[0] == 'l' && buf[1] == 'i')
- elem_size = sizeof(uint32);
+ elem_size = sizeof(uint32_t);
else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
else
@@ -645,14 +683,14 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
ret_val->num_keys = ret_val->num_children;
length = elem_size*ret_val->num_children;
- if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32) < length)
+ if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
{
regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
" cell while parsing subkey-list at offset 0x%.8X.",
offset);
if(strict)
goto fail;
- length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32);
+ length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
}
ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
@@ -660,7 +698,7 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
if(ret_val->elements == NULL)
goto fail;
- elements = (uint8*)malloc(length);
+ elements = (uint8_t*)malloc(length);
if(elements == NULL)
goto fail;
@@ -668,7 +706,7 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
goto fail;
- if(elem_size == sizeof(uint32))
+ if(elem_size == sizeof(uint32_t))
{
for (i=0; i < ret_val->num_children; i++)
{
@@ -698,11 +736,11 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32 offset,
/*******************************************************************
*******************************************************************/
-REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists,
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
REGFI_SUBKEY_LIST** lists,
bool strict)
{
- uint32 i,j,k;
+ uint32_t i,j,k;
REGFI_SUBKEY_LIST* ret_val;
if(lists == NULL)
@@ -754,13 +792,13 @@ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16 num_lists,
/******************************************************************************
*
******************************************************************************/
-REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
+REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size,
bool strict)
{
REGFI_SK_REC* ret_val;
- uint8* sec_desc_buf = NULL;
- uint32 cell_length, length;
- uint8 sk_header[REGFI_SK_MIN_LENGTH];
+ uint8_t* sec_desc_buf = NULL;
+ uint32_t cell_length, length;
+ uint8_t sk_header[REGFI_SK_MIN_LENGTH];
bool unalloc = false;
if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
@@ -791,7 +829,7 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
if(ret_val->cell_size > max_size)
ret_val->cell_size = max_size & 0xFFFFFFF8;
if((ret_val->cell_size < REGFI_SK_MIN_LENGTH)
- || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+ || (strict && (ret_val->cell_size & 0x00000007) != 0))
{
regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
" parsing SK record at offset 0x%.8X.", offset);
@@ -807,8 +845,8 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
ret_val->ref_count = IVAL(sk_header, 0xC);
ret_val->desc_size = IVAL(sk_header, 0x10);
- if(ret_val->prev_sk_off != (ret_val->prev_sk_off & 0xFFFFFFF8)
- || ret_val->next_sk_off != (ret_val->next_sk_off & 0xFFFFFFF8))
+ if((ret_val->prev_sk_off & 0x00000007) != 0
+ || (ret_val->next_sk_off & 0x00000007) != 0)
{
regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
" are not a multiple of 8 while parsing SK record at"
@@ -824,7 +862,7 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
goto fail;
}
- sec_desc_buf = (uint8*)malloc(ret_val->desc_size);
+ sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
if(sec_desc_buf == NULL)
goto fail;
@@ -858,11 +896,11 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32 offset, uint32 max_size,
}
-REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
- uint32 num_values, bool strict)
+REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_values, bool strict)
{
REGFI_VALUE_LIST* ret_val;
- uint32 i, cell_length, length, read_len;
+ uint32_t i, cell_length, length, read_len;
bool unalloc;
if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
@@ -872,7 +910,7 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
return NULL;
}
- if(cell_length != (cell_length & 0xFFFFFFF8))
+ if((cell_length & 0x00000007) != 0)
{
regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
" while parsing value list at offset 0x%.8X.", offset);
@@ -881,16 +919,16 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
cell_length = cell_length & 0xFFFFFFF8;
}
- if((num_values * sizeof(uint32)) > cell_length-sizeof(uint32))
+ if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
{
regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
" while parsing value list at offset 0x%.8X.", offset);
if(strict)
return NULL;
- num_values = cell_length/sizeof(uint32) - sizeof(uint32);
+ num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
}
- read_len = num_values*sizeof(uint32);
+ read_len = num_values*sizeof(uint32_t);
ret_val = talloc(NULL, REGFI_VALUE_LIST);
if(ret_val == NULL)
return NULL;
@@ -904,7 +942,7 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
ret_val->num_values = num_values;
length = read_len;
- if((regfi_read(file->fd, (uint8*)ret_val->elements, &length) != 0)
+ if((regfi_read(file->fd, (uint8_t*)ret_val->elements, &length) != 0)
|| length != read_len)
{
regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
@@ -924,7 +962,7 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
/* XXX: Need to revisit this file length check when we start dealing
* with partial files. */
if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
- || ((ret_val->elements[i] & 0xFFFFFFF8) != ret_val->elements[i]))
+ || ((ret_val->elements[i] & 0x00000007) != 0))
{
regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
" (0x%.8X) found while parsing value list at offset"
@@ -939,70 +977,71 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32 offset,
}
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
+ REGFI_ENCODING output_encoding, bool strict)
+{
+ /* XXX: Registry value names are supposedly limited to 16383 characters
+ * according to:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ * Might want to emit a warning if this is exceeded.
+ * It is expected that "characters" could be variable width.
+ * Also, it may be useful to use this information to limit false positives
+ * when recovering deleted VK records.
+ */
+ int32_t tmp_size;
+ REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
+ ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+
+ if(from_encoding == output_encoding)
+ {
+ vk->valuename_raw = talloc_realloc(vk, vk->valuename_raw,
+ uint8_t, vk->name_length+1);
+ vk->valuename_raw[vk->name_length] = '\0';
+ vk->valuename = (char*)vk->valuename_raw;
+ }
+ else
+ {
+ vk->valuename = talloc_array(vk, char, vk->name_length+1);
+ if(vk->valuename == NULL)
+ {
+ regfi_free_value(vk);
+ return;
+ }
+
+ tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+ regfi_encoding_int2str(output_encoding),
+ vk->valuename_raw, vk->valuename,
+ vk->name_length, vk->name_length+1);
+ if(tmp_size < 0)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
+ " valuename to encoding %s. Error message: %s",
+ regfi_encoding_int2str(output_encoding),
+ strerror(-tmp_size));
+ talloc_free(vk->valuename);
+ vk->valuename = NULL;
+ }
+ }
+}
+
/******************************************************************************
******************************************************************************/
-REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
+REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
+ REGFI_ENCODING output_encoding, bool strict)
{
REGFI_VK_REC* ret_val = NULL;
- const REGFI_HBIN* hbin;
- uint32 data_offset, data_maxsize;
- REGFI_BUFFER data;
+ int32_t max_size;
- hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
- if(!hbin)
+ max_size = regfi_calc_maxsize(file, offset);
+ if(max_size < 0)
return NULL;
- ret_val = regfi_parse_vk(file, offset,
- hbin->block_size + hbin->file_off - offset, strict);
-
+ ret_val = regfi_parse_vk(file, offset, max_size, strict);
if(ret_val == NULL)
return NULL;
- if(ret_val->data_size == 0)
- ret_val->data = NULL;
- else
- {
- if(ret_val->data_in_offset)
- {
- data = regfi_load_data(file, ret_val->type, ret_val->data_off,
- ret_val->data_size, 4,
- ret_val->data_in_offset, strict);
- ret_val->data = data.buf;
- ret_val->data_size = data.len;
- }
- else
- {
- hbin = regfi_lookup_hbin(file, ret_val->data_off);
- if(hbin)
- {
- data_offset = ret_val->data_off+REGFI_REGF_SIZE;
- data_maxsize = hbin->block_size + hbin->file_off - data_offset;
- data = regfi_load_data(file, ret_val->type, data_offset,
- ret_val->data_size, data_maxsize,
- ret_val->data_in_offset, strict);
- ret_val->data = data.buf;
- ret_val->data_size = data.len;
-
- }
- else
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
- " while parsing VK record at offset 0x%.8X.",
- ret_val->offset);
- ret_val->data = NULL;
- }
- }
-
- if(ret_val->data == NULL)
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse data record"
- " while parsing VK record at offset 0x%.8X.",
- ret_val->offset, ret_val->valuename);
- }
- else
- talloc_steal(ret_val, ret_val->data);
- }
+ regfi_interpret_valuename(file, ret_val, output_encoding, strict);
return ret_val;
}
@@ -1011,13 +1050,13 @@ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32 offset, bool strict)
/******************************************************************************
* If !strict, the list may contain NULLs, VK records may point to NULL.
******************************************************************************/
-REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
- uint32 num_values, uint32 max_size,
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
+ uint32_t num_values, uint32_t max_size,
bool strict)
{
- uint32 usable_num_values;
+ uint32_t usable_num_values;
- if((num_values+1) * sizeof(uint32) > max_size)
+ if((num_values+1) * sizeof(uint32_t) > max_size)
{
regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
" parent key (%d) would cause cell to straddle HBIN"
@@ -1025,7 +1064,7 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
" 0x%.8X.", num_values, offset);
if(strict)
return NULL;
- usable_num_values = max_size/sizeof(uint32) - sizeof(uint32);
+ usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t);
}
else
usable_num_values = num_values;
@@ -1034,38 +1073,82 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32 offset,
}
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
+ REGFI_ENCODING output_encoding, bool strict)
+{
+ /* XXX: Registry key names are supposedly limited to 255 characters according to:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ * Might want to emit a warning if this is exceeded.
+ * It is expected that "characters" could be variable width.
+ * Also, it may be useful to use this information to limit false positives
+ * when recovering deleted NK records.
+ */
+ int32_t tmp_size;
+ REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME)
+ ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+
+ if(from_encoding == output_encoding)
+ {
+ nk->keyname_raw = talloc_realloc(nk, nk->keyname_raw, uint8_t, nk->name_length+1);
+ nk->keyname_raw[nk->name_length] = '\0';
+ nk->keyname = (char*)nk->keyname_raw;
+ }
+ else
+ {
+ nk->keyname = talloc_array(nk, char, nk->name_length+1);
+ if(nk->keyname == NULL)
+ {
+ regfi_free_key(nk);
+ return;
+ }
+
+ tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+ regfi_encoding_int2str(output_encoding),
+ nk->keyname_raw, nk->keyname,
+ nk->name_length, nk->name_length+1);
+ if(tmp_size < 0)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
+ " keyname to encoding %s. Error message: %s",
+ regfi_encoding_int2str(output_encoding),
+ strerror(-tmp_size));
+ talloc_free(nk->keyname);
+ nk->keyname = NULL;
+ }
+ }
+}
+
/******************************************************************************
*
******************************************************************************/
-REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
+REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
+ REGFI_ENCODING output_encoding, bool strict)
{
- const REGFI_HBIN* hbin;
- const REGFI_HBIN* sub_hbin;
REGFI_NK_REC* nk;
- uint32 max_length, off;
+ uint32_t off;
+ int32_t max_size;
- hbin = regfi_lookup_hbin(file, offset-REGFI_REGF_SIZE);
- if (hbin == NULL)
+ max_size = regfi_calc_maxsize(file, offset);
+ if (max_size < 0)
return NULL;
/* get the initial nk record */
- max_length = hbin->block_size + hbin->file_off - offset;
- if((nk = regfi_parse_nk(file, offset, max_length, true)) == NULL)
+ if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
{
regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
" offset 0x%.8X.", offset);
return NULL;
}
+ regfi_interpret_keyname(file, nk, output_encoding, strict);
+
/* get value list */
if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
{
- sub_hbin = hbin;
- if(!regfi_offset_in_hbin(hbin, nk->values_off))
- sub_hbin = regfi_lookup_hbin(file, nk->values_off);
-
- if(sub_hbin == NULL)
+ off = nk->values_off + REGFI_REGF_SIZE;
+ max_size = regfi_calc_maxsize(file, off);
+ if(max_size < 0)
{
if(strict)
{
@@ -1078,10 +1161,8 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
}
else
{
- off = nk->values_off + REGFI_REGF_SIZE;
- max_length = sub_hbin->block_size + sub_hbin->file_off - off;
- nk->values = regfi_load_valuelist(file, off, nk->num_values, max_length,
- true);
+ nk->values = regfi_load_valuelist(file, off, nk->num_values,
+ max_size, true);
if(nk->values == NULL)
{
regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
@@ -1099,11 +1180,9 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
/* now get subkey list */
if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE))
{
- sub_hbin = hbin;
- if(!regfi_offset_in_hbin(hbin, nk->subkeys_off))
- sub_hbin = regfi_lookup_hbin(file, nk->subkeys_off);
-
- if(sub_hbin == NULL)
+ off = nk->subkeys_off + REGFI_REGF_SIZE;
+ max_size = regfi_calc_maxsize(file, off);
+ if(max_size < 0)
{
if(strict)
{
@@ -1115,10 +1194,8 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
}
else
{
- off = nk->subkeys_off + REGFI_REGF_SIZE;
- max_length = sub_hbin->block_size + sub_hbin->file_off - off;
nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
- max_length, true);
+ max_size, true);
if(nk->subkeys == NULL)
{
@@ -1136,11 +1213,10 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32 offset, bool strict)
/******************************************************************************
******************************************************************************/
-const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
+const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
{
REGFI_SK_REC* ret_val = NULL;
- const REGFI_HBIN* hbin;
- uint32 max_length;
+ int32_t max_size;
void* failure_ptr = NULL;
/* First look if we have already parsed it */
@@ -1152,12 +1228,11 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
if(ret_val == NULL)
{
- hbin = regfi_lookup_hbin(file, offset - REGFI_REGF_SIZE);
- if(hbin == NULL)
+ max_size = regfi_calc_maxsize(file, offset);
+ if(max_size < 0)
return NULL;
- max_length = hbin->block_size + hbin->file_off - offset;
- ret_val = regfi_parse_sk(file, offset, max_length, strict);
+ ret_val = regfi_parse_sk(file, offset, max_size, strict);
if(ret_val == NULL)
{ /* Cache the parse failure and bail out. */
failure_ptr = talloc(NULL, uint32_t);
@@ -1178,59 +1253,47 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32 offset, bool strict)
/******************************************************************************
******************************************************************************/
-static bool regfi_find_root_nk(REGFI_FILE* file, uint32 offset,uint32 hbin_size,
- uint32* root_offset)
+REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin,
+ REGFI_ENCODING output_encoding)
{
- uint8 tmp[4];
- int32 record_size;
- uint32 length, hbin_offset = 0;
REGFI_NK_REC* nk = NULL;
- bool found = false;
+ uint32_t cell_length;
+ uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
+ uint32_t hbin_end = hbin->file_off+hbin->block_size;
+ bool unalloc;
- for(record_size=0; !found && (hbin_offset < hbin_size); )
+ while(cur_offset < hbin_end)
{
- if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
- return false;
+ if(!regfi_parse_cell(file->fd, cur_offset, NULL, 0, &cell_length, &unalloc))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell at offset"
+ " 0x%.8X while searching for root key.", cur_offset);
+ return NULL;
+ }
- length = 4;
- if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
- return false;
- record_size = IVALS(tmp, 0);
-
- if(record_size < 0)
+ if(!unalloc)
{
- record_size = record_size*(-1);
- nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
+ nk = regfi_load_key(file, cur_offset, output_encoding, true);
if(nk != NULL)
{
- if(nk->key_type & REGFI_NK_FLAG_ROOT)
- {
- found = true;
- *root_offset = nk->offset;
- }
- regfi_free_key(nk);
+ if(nk->flags & REGFI_NK_FLAG_ROOT)
+ return nk;
}
}
- hbin_offset += record_size;
+ cur_offset += cell_length;
}
- return found;
+ return NULL;
}
-/*******************************************************************
- * Open the registry file and then read in the REGF block to get the
- * first hbin offset.
- *******************************************************************/
+/******************************************************************************
+ ******************************************************************************/
REGFI_FILE* regfi_open(const char* filename)
{
- struct stat sbuf;
- REGFI_FILE* rb;
- REGFI_HBIN* hbin = NULL;
- uint32 hbin_off, file_length, cache_secret;
+ REGFI_FILE* ret_val;
int fd;
- bool rla;
/* open an existing file */
if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
@@ -1238,7 +1301,26 @@ REGFI_FILE* regfi_open(const char* filename)
/* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
return NULL;
}
-
+
+ ret_val = regfi_alloc(fd);
+
+ if(ret_val == NULL)
+ close(fd);
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_FILE* regfi_alloc(int fd)
+{
+ struct stat sbuf;
+ REGFI_FILE* rb;
+ REGFI_HBIN* hbin = NULL;
+ uint32_t hbin_off, file_length, cache_secret;
+ bool rla;
+
/* Determine file length. Must be at least big enough
* for the header and one hbin.
*/
@@ -1248,11 +1330,10 @@ REGFI_FILE* regfi_open(const char* filename)
if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
return NULL;
- /* read in an existing file */
+ /* Read file header */
if ((rb = regfi_parse_regf(fd, true)) == NULL)
{
- /* fprintf(stderr, "regfi_open: Failed to read initial REGF block\n"); */
- close(fd);
+ /* fprintf(stderr, "regfi_alloc: Failed to read initial REGF block\n"); */
return NULL;
}
rb->file_length = file_length;
@@ -1260,8 +1341,7 @@ REGFI_FILE* regfi_open(const char* filename)
rb->hbins = range_list_new();
if(rb->hbins == NULL)
{
- /* fprintf(stderr, "regfi_open: Failed to create HBIN list.\n"); */
- close(fd);
+ /* fprintf(stderr, "regfi_alloc: Failed to create HBIN list.\n"); */
talloc_free(rb);
return NULL;
}
@@ -1298,7 +1378,7 @@ REGFI_FILE* regfi_open(const char* filename)
/******************************************************************************
******************************************************************************/
-int regfi_close(REGFI_FILE *file)
+int regfi_close(REGFI_FILE* file)
{
int fd;
@@ -1309,44 +1389,56 @@ int regfi_close(REGFI_FILE *file)
fd = file->fd;
file->fd = -1;
- range_list_free(file->hbins);
+ regfi_free(file);
+
+ return close(fd);
+}
- if(file->sk_cache != NULL)
- lru_cache_destroy(file->sk_cache);
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_free(REGFI_FILE *file)
+{
+ if(file->last_message != NULL)
+ free(file->last_message);
talloc_free(file);
- return close(fd);
}
/******************************************************************************
- * There should be only *one* root key in the registry file based
- * on my experience. --jerry
+ * First checks the offset given by the file header, then checks the
+ * rest of the file if that fails.
******************************************************************************/
-REGFI_NK_REC* regfi_rootkey(REGFI_FILE *file)
+REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
{
REGFI_NK_REC* nk = NULL;
REGFI_HBIN* hbin;
- uint32 root_offset, i, num_hbins;
+ uint32_t root_offset, i, num_hbins;
if(!file)
return NULL;
- /* Scan through the file one HBIN block at a time looking
- * for an NK record with a root key type.
- * This is typically the first NK record in the first HBIN
- * block (but we're not assuming that generally).
+ root_offset = file->root_cell+REGFI_REGF_SIZE;
+ nk = regfi_load_key(file, root_offset, output_encoding, true);
+ if(nk != NULL)
+ {
+ if(nk->flags & REGFI_NK_FLAG_ROOT)
+ return nk;
+ }
+
+ regfi_add_message(file, REGFI_MSG_WARN, "File header indicated root key at"
+ " location 0x%.8X, but no root key found."
+ " Searching rest of file...", root_offset);
+
+ /* If the file header gives bad info, scan through the file one HBIN
+ * block at a time looking for an NK record with a root key type.
*/
num_hbins = range_list_size(file->hbins);
- for(i=0; i < num_hbins; i++)
+ for(i=0; i < num_hbins && nk == NULL; i++)
{
hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
- if(regfi_find_root_nk(file, hbin->file_off+REGFI_HBIN_HEADER_SIZE,
- hbin->block_size-REGFI_HBIN_HEADER_SIZE, &root_offset))
- {
- nk = regfi_load_key(file, root_offset, true);
- break;
- }
+ nk = regfi_find_root_nk(file, hbin, output_encoding);
}
return nk;
@@ -1383,14 +1475,25 @@ void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
/******************************************************************************
*****************************************************************************/
-REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
+ REGFI_ENCODING output_encoding)
{
REGFI_NK_REC* root;
- REGFI_ITERATOR* ret_val = talloc(NULL, REGFI_ITERATOR);
+ REGFI_ITERATOR* ret_val;
+
+ if(output_encoding != REGFI_ENCODING_UTF8
+ && output_encoding != REGFI_ENCODING_ASCII)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Invalid output_encoding supplied"
+ " in creation of regfi iterator.");
+ return NULL;
+ }
+
+ ret_val = talloc(NULL, REGFI_ITERATOR);
if(ret_val == NULL)
return NULL;
- root = regfi_rootkey(fh);
+ root = regfi_rootkey(file, output_encoding);
if(root == NULL)
{
talloc_free(ret_val);
@@ -1405,11 +1508,12 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* fh)
}
talloc_steal(ret_val, ret_val->key_positions);
- ret_val->f = fh;
+ ret_val->f = file;
ret_val->cur_key = root;
ret_val->cur_subkey = 0;
ret_val->cur_value = 0;
-
+ ret_val->string_encoding = output_encoding;
+
return ret_val;
}
@@ -1497,7 +1601,7 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
{
REGFI_NK_REC* subkey;
bool found = false;
- uint32 old_subkey = i->cur_subkey;
+ uint32_t old_subkey = i->cur_subkey;
if(subkey_name == NULL)
return false;
@@ -1531,7 +1635,7 @@ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
*****************************************************************************/
bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
{
- uint32 x;
+ uint32_t x;
if(path == NULL)
return false;
@@ -1584,7 +1688,7 @@ REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
*****************************************************************************/
REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
{
- uint32 nk_offset;
+ uint32_t nk_offset;
/* see if there is anything left to report */
if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
@@ -1593,7 +1697,8 @@ REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
- return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, true);
+ return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, i->string_encoding,
+ true);
}
@@ -1620,6 +1725,7 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
{
REGFI_VK_REC* cur;
bool found = false;
+ uint32_t old_value = i->cur_value;
/* XXX: cur->valuename can be NULL in the registry.
* Should we allow for a way to search for that?
@@ -1639,8 +1745,15 @@ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
cur = regfi_iterator_next_value(i);
}
}
+
+ if(found == false)
+ {
+ i->cur_value = old_value;
+ return false;
+ }
- return found;
+ regfi_free_value(cur);
+ return true;
}
@@ -1658,14 +1771,15 @@ REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
{
REGFI_VK_REC* ret_val = NULL;
- uint32 voffset;
+ uint32_t voffset;
if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
{
if(i->cur_value < i->cur_key->values->num_values)
{
voffset = i->cur_key->values->elements[i->cur_value];
- ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, true);
+ ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE,
+ i->string_encoding, true);
}
}
@@ -1688,13 +1802,368 @@ REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
}
+/******************************************************************************
+ *****************************************************************************/
+REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
+ const REGFI_NK_REC* key)
+{
+ REGFI_CLASSNAME* ret_val;
+ uint8_t* raw;
+ char* interpreted;
+ uint32_t offset;
+ int32_t conv_size, max_size;
+ uint16_t parse_length;
+
+ if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
+ return NULL;
+
+ offset = key->classname_off + REGFI_REGF_SIZE;
+ max_size = regfi_calc_maxsize(i->f, offset);
+ if(max_size <= 0)
+ return NULL;
+
+ parse_length = key->classname_length;
+ raw = regfi_parse_classname(i->f, offset, &parse_length, max_size, true);
+
+ if(raw == NULL)
+ {
+ regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse class"
+ " name at offset 0x%.8X for key record at offset 0x%.8X.",
+ offset, key->offset);
+ return NULL;
+ }
+
+ ret_val = talloc(NULL, REGFI_CLASSNAME);
+ if(ret_val == NULL)
+ return NULL;
+
+ ret_val->raw = raw;
+ ret_val->size = parse_length;
+ talloc_steal(ret_val, raw);
+
+ interpreted = talloc_array(NULL, char, parse_length);
+
+ conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+ regfi_encoding_int2str(i->string_encoding),
+ raw, interpreted,
+ parse_length, parse_length);
+ if(conv_size < 0)
+ {
+ regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred while"
+ " converting classname to charset %s. Error message: %s",
+ i->string_encoding, strerror(-conv_size));
+ talloc_free(interpreted);
+ ret_val->interpreted = NULL;
+ }
+ else
+ {
+ interpreted = talloc_realloc(NULL, interpreted, char, conv_size);
+ ret_val->interpreted = interpreted;
+ talloc_steal(ret_val, interpreted);
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
+ const REGFI_VK_REC* value)
+{
+ REGFI_DATA* ret_val = NULL;
+ REGFI_BUFFER raw_data;
+
+ if(value->data_size != 0)
+ {
+ raw_data = regfi_load_data(i->f, value->data_off, value->data_size,
+ value->data_in_offset, true);
+ if(raw_data.buf == NULL)
+ {
+ regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse data record"
+ " while parsing VK record at offset 0x%.8X.",
+ value->offset);
+ }
+ else
+ {
+ ret_val = regfi_buffer_to_data(raw_data);
+
+ if(ret_val == NULL)
+ {
+ regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred in converting"
+ " data buffer to data structure while interpreting "
+ "data for VK record at offset 0x%.8X.",
+ value->offset);
+ talloc_free(raw_data.buf);
+ return NULL;
+ }
+
+ if(!regfi_interpret_data(i->f, i->string_encoding, value->type, ret_val))
+ {
+ regfi_add_message(i->f, REGFI_MSG_INFO, "Error occurred while"
+ " interpreting data for VK record at offset 0x%.8X.",
+ value->offset);
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_classname(REGFI_CLASSNAME* classname)
+{
+ talloc_free(classname);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_data(REGFI_DATA* data)
+{
+ talloc_free(data);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
+{
+ REGFI_DATA* ret_val;
+
+ if(raw_data.buf == NULL)
+ return NULL;
+
+ ret_val = talloc(NULL, REGFI_DATA);
+ if(ret_val == NULL)
+ return NULL;
+
+ talloc_steal(ret_val, raw_data.buf);
+ ret_val->raw = raw_data.buf;
+ ret_val->size = raw_data.len;
+ ret_val->interpreted_size = 0;
+ ret_val->interpreted.qword = 0;
+
+ return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
+ uint32_t type, REGFI_DATA* data)
+{
+ uint8_t** tmp_array;
+ uint8_t* tmp_str;
+ int32_t tmp_size;
+ uint32_t i, j, array_size;
+
+ if(data == NULL)
+ return false;
+
+ switch (type)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ /* REG_LINK is a symbolic link, stored as a unicode string. */
+ case REG_LINK:
+ tmp_str = talloc_array(NULL, uint8_t, data->size);
+ if(tmp_str == NULL)
+ {
+ data->interpreted.string = NULL;
+ data->interpreted_size = 0;
+ return false;
+ }
+
+ tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+ regfi_encoding_int2str(string_encoding),
+ data->raw, (char*)tmp_str,
+ data->size, data->size);
+ if(tmp_size < 0)
+ {
+ regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
+ " converting data of type %d to %s. Error message: %s",
+ type, string_encoding, strerror(-tmp_size));
+ talloc_free(tmp_str);
+ data->interpreted.string = NULL;
+ data->interpreted_size = 0;
+ return false;
+ }
+
+ tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size);
+ data->interpreted.string = tmp_str;
+ data->interpreted_size = tmp_size;
+ talloc_steal(data, tmp_str);
+ break;
+
+ case REG_DWORD:
+ if(data->size < 4)
+ {
+ data->interpreted.dword = 0;
+ data->interpreted_size = 0;
+ return false;
+ }
+ data->interpreted.dword = IVAL(data->raw, 0);
+ data->interpreted_size = 4;
+ break;
+
+ case REG_DWORD_BE:
+ if(data->size < 4)
+ {
+ data->interpreted.dword_be = 0;
+ data->interpreted_size = 0;
+ return false;
+ }
+ data->interpreted.dword_be = RIVAL(data->raw, 0);
+ data->interpreted_size = 4;
+ break;
+
+ case REG_QWORD:
+ if(data->size < 8)
+ {
+ data->interpreted.qword = 0;
+ data->interpreted_size = 0;
+ return false;
+ }
+ data->interpreted.qword =
+ (uint64_t)IVAL(data->raw, 0) + (((uint64_t)IVAL(data->raw, 4))<<32);
+ data->interpreted_size = 8;
+ break;
+
+ case REG_MULTI_SZ:
+ tmp_str = talloc_array(NULL, uint8_t, data->size);
+ if(tmp_str == NULL)
+ {
+ data->interpreted.multiple_string = NULL;
+ data->interpreted_size = 0;
+ return false;
+ }
+
+ /* Attempt to convert entire string from UTF-16LE to output encoding,
+ * then parse and quote fields individually.
+ */
+ tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+ regfi_encoding_int2str(string_encoding),
+ data->raw, (char*)tmp_str,
+ data->size, data->size);
+ if(tmp_size < 0)
+ {
+ regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
+ " converting data of type %d to %s. Error message: %s",
+ type, string_encoding, strerror(-tmp_size));
+ talloc_free(tmp_str);
+ data->interpreted.multiple_string = NULL;
+ data->interpreted_size = 0;
+ return false;
+ }
+
+ array_size = tmp_size+1;
+ tmp_array = talloc_array(NULL, uint8_t*, array_size);
+ if(tmp_array == NULL)
+ {
+ talloc_free(tmp_str);
+ data->interpreted.string = NULL;
+ data->interpreted_size = 0;
+ return false;
+ }
+
+ tmp_array[0] = tmp_str;
+ for(i=0,j=1; i < tmp_size && j < array_size-1; i++)
+ {
+ if(tmp_str[i] == '\0' && (i+1 < tmp_size))
+ tmp_array[j++] = tmp_str+i+1;
+ }
+ tmp_array[j] = NULL;
+ tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
+ data->interpreted.multiple_string = tmp_array;
+ /* XXX: how meaningful is this? should we store number of strings instead? */
+ data->interpreted_size = tmp_size;
+ talloc_steal(tmp_array, tmp_str);
+ talloc_steal(data, tmp_array);
+ break;
+
+ /* XXX: Dont know how to interpret these yet, just treat as binary */
+ case REG_NONE:
+ data->interpreted.none = data->raw;
+ data->interpreted_size = data->size;
+ break;
+
+ case REG_RESOURCE_LIST:
+ data->interpreted.resource_list = data->raw;
+ data->interpreted_size = data->size;
+ break;
+
+ case REG_FULL_RESOURCE_DESCRIPTOR:
+ data->interpreted.full_resource_descriptor = data->raw;
+ data->interpreted_size = data->size;
+ break;
+
+ case REG_RESOURCE_REQUIREMENTS_LIST:
+ data->interpreted.resource_requirements_list = data->raw;
+ data->interpreted_size = data->size;
+ break;
+
+ case REG_BINARY:
+ data->interpreted.binary = data->raw;
+ data->interpreted_size = data->size;
+ break;
+
+ default:
+ data->interpreted.qword = 0;
+ data->interpreted_size = 0;
+ return false;
+ }
+
+ data->type = type;
+ return true;
+}
+
+
+/******************************************************************************
+ * Convert from UTF-16LE to specified character set.
+ * On error, returns a negative errno code.
+ *****************************************************************************/
+int32_t regfi_conv_charset(const char* input_charset, const char* output_charset,
+ uint8_t* input, char* output,
+ uint32_t input_len, uint32_t output_max)
+{
+ iconv_t conv_desc;
+ char* inbuf = (char*)input;
+ char* outbuf = output;
+ size_t in_len = (size_t)input_len;
+ size_t out_len = (size_t)(output_max-1);
+ int ret;
+
+ /* XXX: Consider creating a couple of conversion descriptors earlier,
+ * storing them on an iterator so they don't have to be recreated
+ * each time.
+ */
+
+ /* Set up conversion descriptor. */
+ conv_desc = iconv_open(output_charset, input_charset);
+
+ ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
+ if(ret == -1)
+ {
+ iconv_close(conv_desc);
+ return -errno;
+ }
+ *outbuf = '\0';
+
+ iconv_close(conv_desc);
+ return output_max-out_len-1;
+}
+
+
+
/*******************************************************************
* Computes the checksum of the registry file header.
- * buffer must be at least the size of an regf header (4096 bytes).
+ * buffer must be at least the size of a regf header (4096 bytes).
*******************************************************************/
-static uint32 regfi_compute_header_checksum(uint8* buffer)
+static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
{
- uint32 checksum, x;
+ uint32_t checksum, x;
int i;
/* XOR of all bytes 0x0000 - 0x01FB */
@@ -1715,8 +2184,8 @@ static uint32 regfi_compute_header_checksum(uint8* buffer)
*******************************************************************/
REGFI_FILE* regfi_parse_regf(int fd, bool strict)
{
- uint8 file_header[REGFI_REGF_SIZE];
- uint32 length;
+ uint8_t file_header[REGFI_REGF_SIZE];
+ uint32_t length;
REGFI_FILE* ret_val;
ret_val = talloc(NULL, REGFI_FILE);
@@ -1791,11 +2260,11 @@ REGFI_FILE* regfi_parse_regf(int fd, bool strict)
* Given real file offset, read and parse the hbin at that location
* along with it's associated cells.
******************************************************************************/
-REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
{
REGFI_HBIN *hbin;
- uint8 hbin_header[REGFI_HBIN_HEADER_SIZE];
- uint32 length;
+ uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
+ uint32_t length;
if(offset >= file->file_length)
return NULL;
@@ -1863,14 +2332,12 @@ REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32 offset, bool strict)
/*******************************************************************
*******************************************************************/
-REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict)
+REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict)
{
- uint8 nk_header[REGFI_NK_MIN_LENGTH];
- const REGFI_HBIN *hbin;
+ uint8_t nk_header[REGFI_NK_MIN_LENGTH];
REGFI_NK_REC* ret_val;
- uint32 length,cell_length;
- uint32 class_offset, class_maxsize;
+ uint32_t length,cell_length;
bool unalloc = false;
if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
@@ -1905,7 +2372,7 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
if(ret_val->cell_size > max_size)
ret_val->cell_size = max_size & 0xFFFFFFF8;
if((ret_val->cell_size < REGFI_NK_MIN_LENGTH)
- || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+ || (strict && (ret_val->cell_size & 0x00000007) != 0))
{
regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
" parsing NK record at offset 0x%.8X.", offset);
@@ -1915,13 +2382,13 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
ret_val->magic[0] = nk_header[0x0];
ret_val->magic[1] = nk_header[0x1];
- ret_val->key_type = SVAL(nk_header, 0x2);
+ ret_val->flags = SVAL(nk_header, 0x2);
- if((ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS) != 0)
+ if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
{
regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
" parsing NK record at offset 0x%.8X.",
- (ret_val->key_type & ~REGFI_NK_KNOWN_FLAGS), offset);
+ (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
}
ret_val->mtime.low = IVAL(nk_header, 0x4);
@@ -1956,6 +2423,7 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
ret_val->name_length = SVAL(nk_header, 0x48);
ret_val->classname_length = SVAL(nk_header, 0x4A);
+ ret_val->keyname = NULL;
if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
{
@@ -1981,8 +2449,8 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
ret_val->cell_size = length;
}
- ret_val->keyname = talloc_array(ret_val, char, ret_val->name_length+1);
- if(ret_val->keyname == NULL)
+ ret_val->keyname_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
+ if(ret_val->keyname_raw == NULL)
{
talloc_free(ret_val);
return NULL;
@@ -1990,7 +2458,7 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
/* Don't need to seek, should be at the right offset */
length = ret_val->name_length;
- if((regfi_read(file->fd, (uint8*)ret_val->keyname, &length) != 0)
+ if((regfi_read(file->fd, (uint8_t*)ret_val->keyname_raw, &length) != 0)
|| length != ret_val->name_length)
{
regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
@@ -1998,52 +2466,21 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32 offset,
talloc_free(ret_val);
return NULL;
}
- ret_val->keyname[ret_val->name_length] = '\0';
-
- /* XXX: This linking should be moved up to regfi_load_key */
- if(ret_val->classname_off != REGFI_OFFSET_NONE)
- {
- hbin = regfi_lookup_hbin(file, ret_val->classname_off);
- if(hbin)
- {
- class_offset = ret_val->classname_off+REGFI_REGF_SIZE;
- class_maxsize = hbin->block_size + hbin->file_off - class_offset;
- ret_val->classname
- = regfi_parse_classname(file, class_offset, &ret_val->classname_length,
- class_maxsize, strict);
- }
- else
- {
- ret_val->classname = NULL;
- regfi_add_message(file, REGFI_MSG_WARN, "Could not find hbin for class"
- " name while parsing NK record at offset 0x%.8X.",
- offset);
- }
-
- if(ret_val->classname == NULL)
- {
- regfi_add_message(file, REGFI_MSG_WARN, "Could not parse class"
- " name while parsing NK record at offset 0x%.8X.",
- offset);
- }
- else
- talloc_steal(ret_val, ret_val->classname);
- }
return ret_val;
}
-char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
- uint16* name_length, uint32 max_size, bool strict)
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+ uint16_t* name_length, uint32_t max_size, bool strict)
{
- char* ret_val = NULL;
- uint32 length;
- uint32 cell_length;
+ uint8_t* ret_val = NULL;
+ uint32_t length;
+ uint32_t cell_length;
bool unalloc = false;
if(*name_length > 0 && offset != REGFI_OFFSET_NONE
- && offset == (offset & 0xFFFFFFF8))
+ && (offset & 0x00000007) == 0)
{
if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
{
@@ -2052,7 +2489,7 @@ char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
return NULL;
}
- if((cell_length & 0xFFFFFFF8) != cell_length)
+ if((cell_length & 0x0000007) != 0)
{
regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
" while parsing class name at offset 0x%.8X.", offset);
@@ -2079,11 +2516,11 @@ char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
*name_length = cell_length - 4;
}
- ret_val = talloc_array(NULL, char, *name_length);
+ ret_val = talloc_array(NULL, uint8_t, *name_length);
if(ret_val != NULL)
{
length = *name_length;
- if((regfi_read(file->fd, (uint8*)ret_val, &length) != 0)
+ if((regfi_read(file->fd, ret_val, &length) != 0)
|| length != *name_length)
{
regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
@@ -2100,12 +2537,12 @@ char* regfi_parse_classname(REGFI_FILE* file, uint32 offset,
/******************************************************************************
*******************************************************************************/
-REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
- uint32 max_size, bool strict)
+REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict)
{
REGFI_VK_REC* ret_val;
- uint8 vk_header[REGFI_VK_MIN_LENGTH];
- uint32 raw_data_size, length, cell_length;
+ uint8_t vk_header[REGFI_VK_MIN_LENGTH];
+ uint32_t raw_data_size, length, cell_length;
bool unalloc = false;
if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
@@ -2122,13 +2559,13 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
ret_val->offset = offset;
ret_val->cell_size = cell_length;
- ret_val->data = NULL;
ret_val->valuename = NULL;
+ ret_val->valuename_raw = NULL;
if(ret_val->cell_size > max_size)
ret_val->cell_size = max_size & 0xFFFFFFF8;
if((ret_val->cell_size < REGFI_VK_MIN_LENGTH)
- || ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8))
+ || (ret_val->cell_size & 0x00000007) != 0)
{
regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
" while parsing VK record at offset 0x%.8X.", offset);
@@ -2153,13 +2590,16 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
ret_val->name_length = SVAL(vk_header, 0x2);
raw_data_size = IVAL(vk_header, 0x4);
ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+ /* The data is typically stored in the offset if the size <= 4,
+ * in which case this flag is set.
+ */
ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
ret_val->data_off = IVAL(vk_header, 0x8);
ret_val->type = IVAL(vk_header, 0xC);
- ret_val->flag = SVAL(vk_header, 0x10);
+ ret_val->flags = SVAL(vk_header, 0x10);
ret_val->unknown1 = SVAL(vk_header, 0x12);
- if(ret_val->flag & REGFI_VK_FLAG_NAME_PRESENT)
+ if(ret_val->name_length > 0)
{
if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
{
@@ -2180,15 +2620,15 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
cell_length+=8;
- ret_val->valuename = talloc_array(ret_val, char, ret_val->name_length+1);
- if(ret_val->valuename == NULL)
+ ret_val->valuename_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
+ if(ret_val->valuename_raw == NULL)
{
talloc_free(ret_val);
return NULL;
}
length = ret_val->name_length;
- if((regfi_read(file->fd, (uint8*)ret_val->valuename, &length) != 0)
+ if((regfi_read(file->fd, (uint8_t*)ret_val->valuename_raw, &length) != 0)
|| length != ret_val->name_length)
{
regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
@@ -2196,8 +2636,6 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
talloc_free(ret_val);
return NULL;
}
- ret_val->valuename[ret_val->name_length] = '\0';
-
}
else
cell_length = REGFI_VK_MIN_LENGTH + 4;
@@ -2214,37 +2652,50 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32 offset,
/******************************************************************************
-*******************************************************************************/
-REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
- uint32 data_type, uint32 offset,
- uint32 length, uint32 max_size,
- bool data_in_offset, bool strict)
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+ uint32_t length, bool data_in_offset,
+ bool strict)
{
REGFI_BUFFER ret_val;
- uint32 read_length, cell_length;
- uint8 i;
+ uint32_t cell_length, offset;
+ int32_t max_size;
bool unalloc;
- /* The data is typically stored in the offset if the size <= 4 */
+ /* Microsoft's documentation indicates that "available memory" is
+ * the limit on value sizes for the more recent registry format version.
+ * This is not only annoying, but it's probably also incorrect, since clearly
+ * value data sizes are limited to 2^31 (high bit used as a flag) and even
+ * with big data records, the apparent max size is:
+ * 16344 * 2^16 = 1071104040 (~1GB).
+ *
+ * We choose to limit it to 1M which was the limit in older versions and
+ * should rarely be exceeded unless the file is corrupt or malicious.
+ * For more info, see:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ */
+ /* XXX: add way to skip this check at user discression. */
+ if(length > REGFI_VK_MAX_DATA_LENGTH)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Value data size %d larger than "
+ "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
+ length = REGFI_VK_MAX_DATA_LENGTH;
+ }
+
if(data_in_offset)
+ return regfi_parse_little_data(file, voffset, length, strict);
+ else
{
- if(length > 4)
+ offset = voffset + REGFI_REGF_SIZE;
+ max_size = regfi_calc_maxsize(file, offset);
+ if(max_size < 0)
{
- regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
- " while parsing data record at offset 0x%.8X.",
- offset);
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
+ " at offset 0x%.8X.", offset);
goto fail;
}
-
- if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
- goto fail;
- ret_val.len = length;
-
- for(i = 0; i < length; i++)
- ret_val.buf[i] = (uint8)((offset >> i*8) & 0xFF);
- }
- else
- {
+
if(!regfi_parse_cell(file->fd, offset, NULL, 0,
&cell_length, &unalloc))
{
@@ -2253,7 +2704,7 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
goto fail;
}
- if((cell_length & 0xFFFFFFF8) != cell_length)
+ if((cell_length & 0x00000007) != 0)
{
regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
" while parsing data record at offset 0x%.8X.",
@@ -2266,10 +2717,7 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
" while parsing data record at offset 0x%.8X.",
offset);
- if(strict)
- goto fail;
- else
- cell_length = max_size;
+ goto fail;
}
if(cell_length - 4 < length)
@@ -2281,7 +2729,8 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
if (file->major_version >= 1 && file->minor_version >= 5)
{
/* Attempt to parse a big data record */
- return regfi_load_big_data(file, offset, length, cell_length, strict);
+ return regfi_load_big_data(file, offset, length, cell_length,
+ NULL, strict);
}
else
{
@@ -2296,19 +2745,7 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
}
}
- if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
- goto fail;
- ret_val.len = length;
-
- read_length = length;
- if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
- || read_length != length)
- {
- regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
- " parsing data record at offset 0x%.8X.", offset);
- talloc_free(ret_val.buf);
- goto fail;
- }
+ ret_val = regfi_parse_data(file, offset, length, strict);
}
return ret_val;
@@ -2321,117 +2758,348 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file,
/******************************************************************************
+ * Parses the common case data records stored in a single cell.
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+ uint32_t length, bool strict)
+{
+ REGFI_BUFFER ret_val;
+ uint32_t read_length;
+
+ ret_val.buf = NULL;
+ ret_val.len = 0;
+
+ if(lseek(file->fd, offset+4, SEEK_SET) == -1)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
+ "reading data at offset 0x%.8X.", offset);
+ return ret_val;
+ }
+
+ if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+ return ret_val;
+ ret_val.len = length;
+
+ read_length = length;
+ if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
+ || read_length != length)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
+ " parsing data record at offset 0x%.8X.", offset);
+ talloc_free(ret_val.buf);
+ ret_val.buf = NULL;
+ ret_val.buf = 0;
+ }
+
+ return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
+ uint32_t length, bool strict)
+{
+ uint8_t i;
+ REGFI_BUFFER ret_val;
+
+ ret_val.buf = NULL;
+ ret_val.len = 0;
+
+ if(length > 4)
+ {
+ regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
+ " while parsing data record. (voffset=0x%.8X, length=%d)",
+ voffset, length);
+ return ret_val;
+ }
+
+ if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+ return ret_val;
+ ret_val.len = length;
+
+ for(i = 0; i < length; i++)
+ ret_val.buf[i] = (uint8_t)((voffset >> i*8) & 0xFF);
+
+ return ret_val;
+}
+
+/******************************************************************************
*******************************************************************************/
-REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
- uint32 offset, uint32 data_length,
- uint32 cell_length, bool strict)
+REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset,
+ uint32_t max_size, bool strict)
{
REGFI_BUFFER ret_val;
- uint16 num_chunks, i;
- uint32 indirect_offset, indirect_length, chunk_length, chunk_offset;
- uint32 read_length, data_left;
+ uint32_t cell_length;
bool unalloc;
- uint32* indirect_ptrs;
- uint8* big_data_cell = (uint8*)malloc(cell_length*sizeof(uint8));
- if(big_data_cell == NULL)
+
+ /* XXX: do something with unalloc? */
+ ret_val.buf = (uint8_t*)talloc_array(NULL, uint8_t, REGFI_BIG_DATA_MIN_LENGTH);
+ if(ret_val.buf == NULL)
+ goto fail;
+
+ if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
+ "while parsing big data header at offset 0x%.8X.",offset);
goto fail;
+ }
- if(!regfi_parse_cell(file->fd, offset, big_data_cell, cell_length-4,
+ if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
&cell_length, &unalloc))
{
regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
- " parsing big data record at offset 0x%.8X.", offset);
- free(big_data_cell);
+ " parsing big data header at offset 0x%.8X.", offset);
goto fail;
}
-
- if((big_data_cell[0] != 'd') || (big_data_cell[1] != 'b'))
+
+ if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
{
regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
" (0x%.2X, 0x%.2X) encountered while parsing"
- " big data record at offset 0x%.8X.",
- big_data_cell[0], big_data_cell[1], offset);
- free(big_data_cell);
+ " big data header at offset 0x%.8X.",
+ ret_val.buf[0], ret_val.buf[1], offset);
goto fail;
}
- num_chunks = SVAL(big_data_cell, 0x2);
- /* XXX: Should check sanity of pointers here and in the indirect
- * block. At least ensure they are a multiple of 8.
- */
- indirect_offset = IVAL(big_data_cell, 0x4) + REGFI_REGF_SIZE;
- free(big_data_cell);
- indirect_ptrs = (uint32*)malloc(num_chunks*sizeof(uint32));
- if(indirect_ptrs == NULL)
+ ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
+ return ret_val;
+
+ fail:
+ if(ret_val.buf != NULL)
+ {
+ talloc_free(ret_val.buf);
+ ret_val.buf = NULL;
+ }
+ ret_val.len = 0;
+ return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
+ uint16_t num_chunks, bool strict)
+{
+ uint32_t* ret_val;
+ uint32_t indirect_length;
+ int32_t max_size;
+ uint16_t i;
+ bool unalloc;
+
+ /* XXX: do something with unalloc? */
+
+ max_size = regfi_calc_maxsize(file, offset);
+ if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
+ return NULL;
+
+ ret_val = (uint32_t*)talloc_array(NULL, uint32_t, num_chunks);
+ if(ret_val == NULL)
goto fail;
- if(!regfi_parse_cell(file->fd, indirect_offset, (uint8*)indirect_ptrs,
- num_chunks*sizeof(uint32),
+ if(!regfi_parse_cell(file->fd, offset, (uint8_t*)ret_val,
+ num_chunks*sizeof(uint32_t),
&indirect_length, &unalloc))
{
regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
" parsing big data indirect record at offset 0x%.8X.",
offset);
- free(indirect_ptrs);
goto fail;
}
-
- if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+
+ /* Convert pointers to proper endianess, verify they are aligned. */
+ for(i=0; i<num_chunks; i++)
{
- free(indirect_ptrs);
- goto fail;
+ ret_val[i] = IVAL(ret_val, i*sizeof(uint32_t));
+ if((ret_val[i] & 0x00000007) != 0)
+ goto fail;
}
- data_left = data_length;
+
+ return ret_val;
- for(i=0; (i<num_chunks) && (data_left>0); i++)
+ fail:
+ if(ret_val != NULL)
+ talloc_free(ret_val);
+ return NULL;
+}
+
+
+/******************************************************************************
+ * Arguments:
+ * file --
+ * offsets -- list of virtual offsets.
+ * num_chunks --
+ * strict --
+ *
+ * Returns:
+ * A range_list with physical offsets and complete lengths
+ * (including cell headers) of associated cells.
+ * No data in range_list elements.
+ ******************************************************************************/
+range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
+ uint16_t num_chunks, bool strict)
+{
+ uint32_t cell_length, chunk_offset;
+ range_list* ret_val;
+ uint16_t i;
+ bool unalloc;
+
+ /* XXX: do something with unalloc? */
+ ret_val = range_list_new();
+ if(ret_val == NULL)
+ goto fail;
+
+ for(i=0; i<num_chunks; i++)
{
- /* Fix endianness of pointer and convert to physical offset */
- chunk_offset = IVAL(indirect_ptrs, i*4) + REGFI_REGF_SIZE;
- if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
- &chunk_length, &unalloc))
+ chunk_offset = offsets[i]+REGFI_REGF_SIZE;
+ if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+ &cell_length, &unalloc))
{
regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
" parsing big data chunk at offset 0x%.8X.",
chunk_offset);
- if(strict)
- goto chunk_fail;
- else
- break;
+ goto fail;
}
- /* XXX: This should be "chunk_length-4" to account for the 4 byte cell
+ if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
+ goto fail;
+ }
+
+ return ret_val;
+
+ fail:
+ if(ret_val != NULL)
+ range_list_free(ret_val);
+ return NULL;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
+ uint32_t offset, uint32_t data_length,
+ uint32_t cell_length, range_list* used_ranges,
+ bool strict)
+{
+ REGFI_BUFFER ret_val;
+ uint16_t num_chunks, i;
+ uint32_t read_length, data_left, tmp_len, indirect_offset;
+ uint32_t* indirect_ptrs = NULL;
+ REGFI_BUFFER bd_header;
+ range_list* bd_cells = NULL;
+ const range_list_element* cell_info;
+
+ ret_val.buf = NULL;
+
+ /* XXX: Add better error/warning messages */
+
+ bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
+ if(bd_header.buf == NULL)
+ goto fail;
+
+ /* Keep track of used space for use by reglookup-recover */
+ if(used_ranges != NULL)
+ if(!range_list_add(used_ranges, offset, cell_length, NULL))
+ goto fail;
+
+ num_chunks = SVAL(bd_header.buf, 0x2);
+ indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
+ talloc_free(bd_header.buf);
+
+ indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
+ num_chunks, strict);
+ if(indirect_ptrs == NULL)
+ goto fail;
+
+ if(used_ranges != NULL)
+ if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
+ goto fail;
+
+ if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+ goto fail;
+ data_left = data_length;
+
+ bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
+ if(bd_cells == NULL)
+ goto fail;
+
+ talloc_free(indirect_ptrs);
+ indirect_ptrs = NULL;
+
+ for(i=0; (i<num_chunks) && (data_left>0); i++)
+ {
+ cell_info = range_list_get(bd_cells, i);
+ if(cell_info == NULL)
+ goto fail;
+
+ /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
* length. However, it has been observed that some (all?) chunks
* have an additional 4 bytes of 0 at the end of their cells that
* isn't part of the data, so we're trimming that off too.
+ * Perhaps it's just an 8 byte alignment requirement...
*/
- if(chunk_length-8 >= data_left)
+ if(cell_info->length - 8 >= data_left)
+ {
+ if(i+1 != num_chunks)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
+ "while constructing big data at offset 0x%.8X "
+ "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ }
read_length = data_left;
+ }
else
- read_length = chunk_length-8;
+ read_length = cell_info->length - 8;
+
+ if(read_length > regfi_calc_maxsize(file, cell_info->offset))
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
+ "while constructing big data at offset 0x%.8X "
+ "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ goto fail;
+ }
+
+ if(lseek(file->fd, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
+ {
+ regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
+ "constructing big data at offset 0x%.8X "
+ "(chunk offset 0x%.8X).", offset, cell_info->offset);
+ goto fail;
+ }
+
+ tmp_len = read_length;
if(regfi_read(file->fd, ret_val.buf+(data_length-data_left),
- &read_length) != 0)
+ &read_length) != 0 || (read_length != tmp_len))
{
regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
- " constructing big data at offset 0x%.8X.",
- chunk_offset);
- if(strict)
- goto chunk_fail;
- else
- break;
+ " constructing big data at offset 0x%.8X"
+ " (chunk offset 0x%.8X).", offset, cell_info->offset);
+ goto fail;
}
+ if(used_ranges != NULL)
+ if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
+ goto fail;
+
data_left -= read_length;
}
- free(indirect_ptrs);
- ret_val.len = data_length-data_left;
+ range_list_free(bd_cells);
+ ret_val.len = data_length-data_left;
return ret_val;
- chunk_fail:
- free(indirect_ptrs);
- talloc_free(ret_val.buf);
fail:
+ if(ret_val.buf != NULL)
+ talloc_free(ret_val.buf);
+ if(indirect_ptrs != NULL)
+ talloc_free(indirect_ptrs);
+ if(bd_cells != NULL)
+ range_list_free(bd_cells);
ret_val.buf = NULL;
ret_val.len = 0;
return ret_val;
@@ -2443,7 +3111,7 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
range_list* ret_val;
REGFI_HBIN* hbin;
const range_list_element* hbins_elem;
- uint32 i, num_hbins, curr_off, cell_len;
+ uint32_t i, num_hbins, curr_off, cell_len;
bool is_unalloc;
ret_val = range_list_new();
@@ -2465,7 +3133,7 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
&cell_len, &is_unalloc))
break;
- if((cell_len == 0) || ((cell_len & 0xFFFFFFF8) != cell_len))
+ if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
{
regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
" while parsing unallocated cells at offset 0x%.8X.",
@@ -2490,3 +3158,104 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
return ret_val;
}
+
+
+/* From lib/time.c */
+
+/****************************************************************************
+ Put a 8 byte filetime from a time_t
+ This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
+{
+ double d;
+
+ if (t==0)
+ {
+ nt->low = 0;
+ nt->high = 0;
+ return;
+ }
+
+ if (t == TIME_T_MAX)
+ {
+ nt->low = 0xffffffff;
+ nt->high = 0x7fffffff;
+ return;
+ }
+
+ if (t == -1)
+ {
+ nt->low = 0xffffffff;
+ nt->high = 0xffffffff;
+ return;
+ }
+
+ /* this converts GMT to kludge-GMT */
+ /* XXX: This was removed due to difficult dependency requirements.
+ * So far, times appear to be correct without this adjustment, but
+ * that may be proven wrong with adequate testing.
+ */
+ /* t -= TimeDiff(t) - get_serverzone(); */
+
+ d = (double)(t);
+ d += TIME_FIXUP_CONSTANT;
+ d *= 1.0e7;
+
+ nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30))));
+ nt->low = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+
+/****************************************************************************
+ Interpret an 8 byte "filetime" structure to a time_t
+ It's originally in "100ns units since jan 1st 1601"
+
+ An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
+
+ It appears to be kludge-GMT (at least for file listings). This means
+ its the GMT you get by taking a localtime and adding the
+ serverzone. This is NOT the same as GMT in some cases. This routine
+ converts this to real GMT.
+****************************************************************************/
+time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
+{
+ double d;
+ time_t ret;
+ /* The next two lines are a fix needed for the
+ broken SCO compiler. JRA. */
+ time_t l_time_min = TIME_T_MIN;
+ time_t l_time_max = TIME_T_MAX;
+
+ if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
+ return(0);
+
+ d = ((double)nt->high)*4.0*(double)(1<<30);
+ d += (nt->low&0xFFF00000);
+ d *= 1.0e-7;
+
+ /* now adjust by 369 years to make the secs since 1970 */
+ d -= TIME_FIXUP_CONSTANT;
+
+ if (d <= l_time_min)
+ return (l_time_min);
+
+ if (d >= l_time_max)
+ return (l_time_max);
+
+ ret = (time_t)(d+0.5);
+
+ /* this takes us from kludge-GMT to real GMT */
+ /* XXX: This was removed due to difficult dependency requirements.
+ * So far, times appear to be correct without this adjustment, but
+ * that may be proven wrong with adequate testing.
+ */
+ /*
+ ret -= get_serverzone();
+ ret += LocTimeDiff(ret);
+ */
+
+ return(ret);
+}
+
+/* End of stuff from lib/time.c */
diff --git a/lib/smb_deps.c b/lib/smb_deps.c
deleted file mode 100644
index eb09aa4..0000000
--- a/lib/smb_deps.c
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * This file contains miscellaneous pieces of code which regfio.c
- * depends upon, from the Samba Subversion tree. See:
- * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
- *
- * Copyright (C) 2005-2006,2009 Timothy D. Morgan
- * Copyright (C) 1992-2005 Samba development team
- * (see individual files under Subversion for details.)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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.
- *
- * $Id: smb_deps.c 147 2009-02-22 19:31:52Z tim $
- */
-
-#include "smb_deps.h"
-
-
-/* These act as replacements for numerous Samba memory allocation
- * functions.
- */
-void* zalloc(size_t size)
-{
- void* ret_val = NULL;
- if((size > 0) && (ret_val = (void*)malloc(size)) != NULL)
- memset(ret_val, 0, size);
-
- return ret_val;
-}
-
-void* zcalloc(size_t size, unsigned int count)
-{
- return zalloc(size*count);
-}
-
-/* From lib/time.c */
-
-/****************************************************************************
- Put a 8 byte filetime from a time_t
- This takes real GMT as input and converts to kludge-GMT
-****************************************************************************/
-void unix_to_nt_time(NTTIME *nt, time_t t)
-{
- double d;
-
- if (t==0)
- {
- nt->low = 0;
- nt->high = 0;
- return;
- }
-
- if (t == TIME_T_MAX)
- {
- nt->low = 0xffffffff;
- nt->high = 0x7fffffff;
- return;
- }
-
- if (t == -1)
- {
- nt->low = 0xffffffff;
- nt->high = 0xffffffff;
- return;
- }
-
- /* this converts GMT to kludge-GMT */
- /* XXX: This was removed due to difficult dependency requirements.
- * So far, times appear to be correct without this adjustment, but
- * that may be proven wrong with adequate testing.
- */
- /* t -= TimeDiff(t) - get_serverzone(); */
-
- d = (double)(t);
- d += TIME_FIXUP_CONSTANT;
- d *= 1.0e7;
-
- nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
- nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
-}
-
-
-/****************************************************************************
- Interpret an 8 byte "filetime" structure to a time_t
- It's originally in "100ns units since jan 1st 1601"
-
- An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
-
- It appears to be kludge-GMT (at least for file listings). This means
- its the GMT you get by taking a localtime and adding the
- serverzone. This is NOT the same as GMT in some cases. This routine
- converts this to real GMT.
-****************************************************************************/
-time_t nt_time_to_unix(const NTTIME* nt)
-{
- double d;
- time_t ret;
- /* The next two lines are a fix needed for the
- broken SCO compiler. JRA. */
- time_t l_time_min = TIME_T_MIN;
- time_t l_time_max = TIME_T_MAX;
-
- if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
- return(0);
-
- d = ((double)nt->high)*4.0*(double)(1<<30);
- d += (nt->low&0xFFF00000);
- d *= 1.0e-7;
-
- /* now adjust by 369 years to make the secs since 1970 */
- d -= TIME_FIXUP_CONSTANT;
-
- if (d <= l_time_min)
- return (l_time_min);
-
- if (d >= l_time_max)
- return (l_time_max);
-
- ret = (time_t)(d+0.5);
-
- /* this takes us from kludge-GMT to real GMT */
- /* XXX: This was removed due to difficult dependency requirements.
- * So far, times appear to be correct without this adjustment, but
- * that may be proven wrong with adequate testing.
- */
- /*
- ret -= get_serverzone();
- ret += LocTimeDiff(ret);
- */
-
- return(ret);
-}
-
-/* End of stuff from lib/time.c */
diff --git a/lib/void_stack.c b/lib/void_stack.c
index 34c792c..bdb7e9b 100644
--- a/lib/void_stack.c
+++ b/lib/void_stack.c
@@ -1,9 +1,5 @@
/*
- * This is a really simple implementation of a stack which stores chunks
- * of memory of any type. It still needs work to eliminate memory
- * leaks.
- *
- * Copyright (C) 2005,2007,2009 Timothy D. Morgan
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
*
* 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
@@ -18,9 +14,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: void_stack.c 150 2009-03-02 02:17:46Z tim $
+ * $Id: void_stack.c 169 2010-03-03 19:24:58Z tim $
+ */
+
+/**
+ * @file
*/
+
#include "void_stack.h"
void_stack* void_stack_new(unsigned short max_size)
diff --git a/lib/winsec.c b/lib/winsec.c
index dfd57a7..2aea7ba 100644
--- a/lib/winsec.c
+++ b/lib/winsec.c
@@ -1,9 +1,6 @@
/*
- * This file contains refactored Samba code used to interpret Windows
- * Security Descriptors. See:
- * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
*
- * Copyright (C) 2005-2006,2009 Timothy D. Morgan
+ * Copyright (C) 2005-2006,2009-2010 Timothy D. Morgan
* Copyright (C) 1992-2005 Samba development team
* (see individual files under Subversion for details.)
*
@@ -20,9 +17,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: winsec.c 148 2009-02-22 23:22:59Z tim $
+ * $Id: winsec.c 169 2010-03-03 19:24:58Z tim $
*/
+/** @file */
+
#include "winsec.h"
diff --git a/src/common.c b/src/common.c
index 734ddb1..a0e40e5 100644
--- a/src/common.c
+++ b/src/common.c
@@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: common.c 154 2009-06-03 15:21:47Z tim $
+ * $Id: common.c 172 2010-03-08 03:04:34Z tim $
*/
#include <iconv.h>
@@ -28,7 +28,7 @@ const char* key_special_chars = ",\"\\/";
const char* subfield_special_chars = ",\"\\|";
const char* common_special_chars = ",\"\\";
-#define REGLOOKUP_VERSION "0.11.0"
+#define REGLOOKUP_VERSION "0.12.0"
#define REGLOOKUP_EXIT_OK 0
#define REGLOOKUP_EXIT_OSERR 71
@@ -144,81 +144,6 @@ static char* quote_string(const char* str, const char* special)
/*
- * Convert from UTF-16LE to ASCII. Accepts a Unicode buffer, uni, and
- * it's length, uni_max. Writes ASCII to the buffer ascii, whose size
- * is ascii_max. Writes at most (ascii_max-1) bytes to ascii, and null
- * terminates the string. Returns the length of the data written to
- * ascii. On error, returns a negative errno code.
- */
-static int uni_to_ascii(unsigned char* uni, char* ascii,
- uint32 uni_max, uint32 ascii_max)
-{
- char* inbuf = (char*)uni;
- char* outbuf = ascii;
- size_t in_len = (size_t)uni_max;
- size_t out_len = (size_t)(ascii_max-1);
- int ret;
-
- /* Set up conversion descriptor. */
- conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
-
- ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
- if(ret == -1)
- {
- iconv_close(conv_desc);
- return -errno;
- }
- *outbuf = '\0';
-
- iconv_close(conv_desc);
- return ascii_max-out_len-1;
-}
-
-
-static char* quote_unicode(unsigned char* uni, uint32 length,
- const char* special, char** error_msg)
-{
- char* ret_val;
- char* ascii = NULL;
- char* tmp_err;
- int ret_err;
- *error_msg = NULL;
-
- if(length+1 > 0)
- ascii = malloc(length+1);
- if(ascii == NULL)
- {
- *error_msg = (char*)malloc(27);
- if(*error_msg == NULL)
- return NULL;
- strcpy(*error_msg, "Memory allocation failure.");
- return NULL;
- }
-
- ret_err = uni_to_ascii(uni, ascii, length, length+1);
- if(ret_err < 0)
- {
- free(ascii);
- tmp_err = strerror(-ret_err);
- *error_msg = (char*)malloc(61+strlen(tmp_err));
- if(*error_msg == NULL)
- return NULL;
-
- sprintf(*error_msg,
- "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
- ret_val = quote_buffer(uni, length, special);
- }
- else
- {
- ret_val = quote_string(ascii, special);
- free(ascii);
- }
-
- return ret_val;
-}
-
-
-/*
* Convert a data value to a string for display. Returns NULL on error,
* and the string to display if there is no error, or a non-fatal
* error. On any error (fatal or non-fatal) occurs, (*error_msg) will
@@ -228,172 +153,186 @@ static char* quote_unicode(unsigned char* uni, uint32 length,
* is the responsibility of the caller to free both a non-NULL return
* value, and a non-NULL (*error_msg).
*/
-/* XXX: Part of this function's logic should be pushed into the regfi API.
- * The structures should be parsed and stored with VK records and only
- * escaped/encoded later in reglookup and reglookup-recover.
- */
-static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type,
- char** error_msg)
+static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
{
- char* asciip;
- char* ascii;
- char* ascii_tmp;
+ char* ret_val;
char* cur_quoted;
- char* tmp_err = NULL;
- const char* delim;
- uint32 i;
- uint32 cur_str_len;
- uint32 ascii_max;
- uint32 str_rem, alen;
- int ret_err;
-
- if(datap == NULL)
+ char* tmp_ptr;
+ char* delim;
+ uint32_t ret_val_left, i, tmp_len;
+
+ if(data == NULL || data->size == 0)
{
- *error_msg = (char*)malloc(24);
+ *error_msg = (char*)malloc(37);
if(*error_msg == NULL)
return NULL;
- strcpy(*error_msg, "Data pointer was NULL.");
+ strcpy(*error_msg, "Data pointer was NULL or size was 0.");
return NULL;
}
*error_msg = NULL;
- switch (type)
+
+ if(data->interpreted_size == 0)
+ {
+ *error_msg = (char*)malloc(51);
+ if(*error_msg == NULL)
+ return NULL;
+ strcpy(*error_msg, "Data could not be interpreted, quoting raw buffer.");
+ return quote_buffer(data->raw, data->size, subfield_special_chars);
+ }
+
+ switch (data->type)
{
case REG_SZ:
+ ret_val = quote_string((char*)data->interpreted.string, common_special_chars);
+ if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+ strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+ return ret_val;
+ break;
+
+
case REG_EXPAND_SZ:
- /* REG_LINK is a symbolic link, stored as a unicode string. */
+ ret_val = quote_string((char*)data->interpreted.expand_string,
+ common_special_chars);
+ if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+ strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+ return ret_val;
+ break;
+
case REG_LINK:
- /* Sometimes values have binary stored in them. If the unicode
- * conversion fails, just quote it raw.
- */
- cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
- if(cur_quoted == NULL)
- {
- if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+ ret_val = quote_string((char*)data->interpreted.link, common_special_chars);
+ if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
- else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
- {
- sprintf(*error_msg, "Buffer could not be quoted due to error: %s",
- tmp_err);
- free(tmp_err);
- }
- }
- else if (tmp_err != NULL)
- *error_msg = tmp_err;
- return cur_quoted;
+
+ return ret_val;
break;
case REG_DWORD:
- ascii_max = sizeof(char)*(8+2+1);
- ascii = malloc(ascii_max);
- if(ascii == NULL)
+ ret_val = malloc(sizeof(char)*(8+2+1));
+ if(ret_val == NULL)
return NULL;
- snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
- datap[3], datap[2], datap[1], datap[0]);
- return ascii;
+ sprintf(ret_val, "0x%.8X", data->interpreted.dword);
+ return ret_val;
break;
case REG_DWORD_BE:
- ascii_max = sizeof(char)*(8+2+1);
- ascii = malloc(ascii_max);
- if(ascii == NULL)
+ ret_val = malloc(sizeof(char)*(8+2+1));
+ if(ret_val == NULL)
return NULL;
- snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
- datap[0], datap[1], datap[2], datap[3]);
- return ascii;
+ sprintf(ret_val, "0x%.8X", data->interpreted.dword_be);
+ return ret_val;
break;
case REG_QWORD:
- ascii_max = sizeof(char)*(16+2+1);
- ascii = malloc(ascii_max);
- if(ascii == NULL)
+ ret_val = malloc(sizeof(char)*(16+2+1));
+ if(ret_val == NULL)
return NULL;
- snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
- datap[7], datap[6], datap[5], datap[4],
- datap[3], datap[2], datap[1], datap[0]);
- return ascii;
+ sprintf(ret_val, "0x%.16llX",
+ (long long unsigned int)data->interpreted.qword);
+ return ret_val;
break;
-
+
case REG_MULTI_SZ:
- ascii_max = sizeof(char)*(len*4+1);
- ascii_tmp = malloc(ascii_max);
- if(ascii_tmp == NULL)
+ ret_val_left = data->interpreted_size*4+1;
+ ret_val = malloc(ret_val_left);
+ if(ret_val == NULL)
return NULL;
- /* Attempt to convert entire string from UTF-16LE to ASCII,
- * then parse and quote fields individually.
- * If this fails, simply quote entire buffer as binary.
- */
- ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
- if(ret_err < 0)
+ tmp_ptr = ret_val;
+ tmp_ptr[0] = '\0';
+ delim = "";
+ for(i=0; data->interpreted.multiple_string[i] != NULL; i++)
{
- tmp_err = strerror(-ret_err);
- *error_msg = (char*)malloc(61+strlen(tmp_err));
- if(*error_msg == NULL)
+ cur_quoted = quote_string((char*)data->interpreted.multiple_string[i],
+ subfield_special_chars);
+ if(cur_quoted != NULL && cur_quoted[0] != '\0')
{
- free(ascii_tmp);
- return NULL;
- }
-
- sprintf(*error_msg, "MULTI_SZ unicode conversion"
- " failed with '%s'. Quoting as binary.", tmp_err);
- ascii = quote_buffer(datap, len, subfield_special_chars);
- }
- else
- {
- ascii = malloc(ascii_max);
- if(ascii == NULL)
- {
- free(ascii_tmp);
- return NULL;
- }
- asciip = ascii;
- asciip[0] = '\0';
- str_rem = ascii_max;
- delim = "";
- for(i=0; i<ret_err; i+=cur_str_len+1)
- {
- cur_str_len = strlen(ascii_tmp+i);
- if(ascii_tmp[i] != '\0')
- {
- cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
- if(cur_quoted != NULL)
- {
- alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
- asciip += alen;
- str_rem -= alen;
- free(cur_quoted);
- }
- }
- delim = "|";
+ tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted);
+ tmp_ptr += tmp_len;
+ ret_val_left -= tmp_len;
+ free(cur_quoted);
}
+ delim = "|";
}
- free(ascii_tmp);
- return ascii;
+ return ret_val;
break;
- /* XXX: Dont know what to do with these yet, just print as binary... */
- default:
- *error_msg = (char*)malloc(65);
- if(*error_msg == NULL)
- return NULL;
- sprintf(*error_msg,
- "Unrecognized registry data type (0x%.8X); quoting as binary.",
- type);
case REG_NONE:
+ return quote_buffer(data->interpreted.none, data->interpreted_size,
+ common_special_chars);
+
+ break;
+
case REG_RESOURCE_LIST:
+ return quote_buffer(data->interpreted.resource_list, data->interpreted_size,
+ common_special_chars);
+
+ break;
+
case REG_FULL_RESOURCE_DESCRIPTOR:
+ return quote_buffer(data->interpreted.full_resource_descriptor,
+ data->interpreted_size, common_special_chars);
+
+ break;
+
case REG_RESOURCE_REQUIREMENTS_LIST:
+ return quote_buffer(data->interpreted.resource_requirements_list,
+ data->interpreted_size, common_special_chars);
+
+ break;
case REG_BINARY:
- return quote_buffer(datap, len, common_special_chars);
+ return quote_buffer(data->interpreted.binary, data->interpreted_size,
+ common_special_chars);
+
break;
- }
+ default:
+ /* This shouldn't happen, since the regfi routines won't interpret
+ * unknown types, but just as a safety measure against library changes...
+ */
+ *error_msg = (char*)malloc(65);
+ if(*error_msg == NULL)
+ return NULL;
+ sprintf(*error_msg,
+ "Unrecognized registry data type (0x%.8X); quoting as binary.",
+ data->type);
+ return quote_buffer(data->raw, data->size, common_special_chars);
+ }
+
return NULL;
}
+
+
+static char* get_quoted_keyname(const REGFI_NK_REC* nk)
+{
+ char* ret_val;
+
+ if(nk->keyname == NULL)
+ ret_val = quote_buffer(nk->keyname_raw, nk->name_length, key_special_chars);
+ else
+ ret_val = quote_string(nk->keyname, key_special_chars);
+
+ return ret_val;
+}
+
+
+static char* get_quoted_valuename(const REGFI_VK_REC* vk)
+{
+ char* ret_val;
+
+ if(vk->valuename == NULL)
+ ret_val = quote_buffer(vk->valuename_raw, vk->name_length,
+ key_special_chars);
+ else
+ ret_val = quote_string(vk->valuename, key_special_chars);
+
+ return ret_val;
+}
diff --git a/src/reglookup-recover.c b/src/reglookup-recover.c
index b549a78..0cd06f2 100644
--- a/src/reglookup-recover.c
+++ b/src/reglookup-recover.c
@@ -1,7 +1,7 @@
/*
* This program attempts to recover deleted data structures in a registry hive.
*
- * Copyright (C) 2008-2009 Timothy D. Morgan
+ * Copyright (C) 2008-2010 Timothy D. Morgan
*
* 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
@@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: reglookup-recover.c 152 2009-06-02 20:00:38Z tim $
+ * $Id: reglookup-recover.c 173 2010-03-08 03:39:09Z tim $
*/
#include <stdio.h>
@@ -39,16 +39,16 @@ char* registry_file = NULL;
#include "common.c"
-char* getQuotedData(int fd, uint32 offset, uint32 length)
+char* getQuotedData(int fd, uint32_t offset, uint32_t length)
{
- uint8* buf;
+ uint8_t* buf;
char* quoted_buf;
- uint32 len;
+ uint32_t len;
if((lseek(fd, offset, SEEK_SET)) == -1)
return NULL;
- buf = (uint8*)malloc(length);
+ buf = (uint8_t*)malloc(length);
if(buf == NULL)
return NULL;
@@ -65,7 +65,7 @@ char* getQuotedData(int fd, uint32 offset, uint32 length)
return quoted_buf;
}
-
+/* XXX: Somewhere in here, need to start looking for and handling classnames */
void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
{
char mtime[20];
@@ -74,11 +74,14 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
char* quoted_name = NULL;
char* quoted_raw = "";
- *tmp_time = nt_time_to_unix(&nk->mtime);
+ *tmp_time = regfi_nt2unix_time(&nk->mtime);
tmp_time_s = gmtime(tmp_time);
strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
- quoted_name = quote_string(nk->keyname, key_special_chars);
+ /* XXX: Add command line option to choose output encoding */
+ regfi_interpret_keyname(f, nk, REGFI_ENCODING_ASCII, true);
+
+ quoted_name = get_quoted_keyname(nk);
if (quoted_name == NULL)
{
quoted_name = malloc(1*sizeof(char));
@@ -103,34 +106,18 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
}
-void printValue(REGFI_FILE* f, const REGFI_VK_REC* vk, const char* prefix)
+void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
{
char* quoted_value = NULL;
char* quoted_name = NULL;
char* quoted_raw = "";
char* conv_error = NULL;
const char* str_type = NULL;
- uint32 size = vk->data_size;
- /* Microsoft's documentation indicates that "available memory" is
- * the limit on value sizes. Annoying. We limit it to 1M which
- * should rarely be exceeded, unless the file is corrupt or
- * malicious. For more info, see:
- * http://msdn2.microsoft.com/en-us/library/ms724872.aspx
- */
- /* XXX: Should probably do something different here for this tool.
- * Also, It would be really nice if this message somehow included the
- * name of the current value we're having trouble with, since
- * stderr/stdout don't always sync nicely.
- */
- if(size > REGFI_VK_MAX_DATA_LENGTH)
- {
- fprintf(stderr, "WARN: value data size %d larger than "
- "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
- size = REGFI_VK_MAX_DATA_LENGTH;
- }
+ /* XXX: Add command line option to choose output encoding */
+ regfi_interpret_valuename(f, vk, REGFI_ENCODING_ASCII, true);
- quoted_name = quote_string(vk->valuename, key_special_chars);
+ quoted_name = get_quoted_valuename(vk);
if (quoted_name == NULL)
{ /* Value names are NULL when we're looking at the "(default)" value.
* Currently we just return a 0-length string to try an eliminate
@@ -138,13 +125,21 @@ void printValue(REGFI_FILE* f, const REGFI_VK_REC* vk, const char* prefix)
* in the output allows one to differentiate between the parent key and
* this value.
*/
- quoted_name = malloc(1*sizeof(char));
+ quoted_name = strdup("");
if(quoted_name == NULL)
bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
- quoted_name[0] = '\0';
}
- quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+ /* XXX: Add command line option to choose output encoding */
+ if(vk->data != NULL
+ && !regfi_interpret_data(f, REGFI_ENCODING_ASCII, vk->type, vk->data))
+ {
+ fprintf(stderr, "WARN: Error occurred while interpreting data for VK record"
+ " at offset 0x%.8X.\n", vk->offset);
+ }
+ printMsgs(f);
+
+ quoted_value = data_to_ascii(vk->data, &conv_error);
if(quoted_value == NULL)
{
quoted_value = malloc(1*sizeof(char));
@@ -227,10 +222,10 @@ void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
}
-int printCell(REGFI_FILE* f, uint32 offset)
+int printCell(REGFI_FILE* f, uint32_t offset)
{
char* quoted_buf;
- uint32 cell_length;
+ uint32_t cell_length;
bool unalloc;
if(!regfi_parse_cell(f->fd, offset, NULL, 0, &cell_length, &unalloc))
@@ -257,43 +252,44 @@ int printCell(REGFI_FILE* f, uint32 offset)
char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
{
void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
- const REGFI_HBIN* hbin;
REGFI_NK_REC* cur_ancestor;
char* ret_val;
- uint32 virt_offset, i, stack_size, ret_val_size, ret_val_used;
- uint32 max_length;
+ uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset;
+ int32_t max_size;
REGFI_BUFFER* path_element;
/* The path_stack size limit should guarantee that we don't recurse forever. */
virt_offset = nk->parent_off;
ret_val_size = 1; /* NUL */
while(virt_offset != REGFI_OFFSET_NONE)
- {
- hbin = regfi_lookup_hbin(f, virt_offset);
- if(hbin == NULL)
+ {
+ offset = virt_offset+REGFI_REGF_SIZE;
+ max_size = regfi_calc_maxsize(f, offset);
+ if(max_size < 0)
virt_offset = REGFI_OFFSET_NONE;
else
{
- max_length = hbin->block_size + hbin->file_off
- - (virt_offset+REGFI_REGF_SIZE);
- cur_ancestor = regfi_parse_nk(f, virt_offset+REGFI_REGF_SIZE,
- max_length, true);
+ cur_ancestor = regfi_parse_nk(f, offset, max_size, true);
printMsgs(f);
if(cur_ancestor == NULL)
virt_offset = REGFI_OFFSET_NONE;
else
{
- if(cur_ancestor->key_type & REGFI_NK_FLAG_ROOT)
+ if(cur_ancestor->flags & REGFI_NK_FLAG_ROOT)
virt_offset = REGFI_OFFSET_NONE;
else
virt_offset = cur_ancestor->parent_off;
path_element = talloc(path_stack, REGFI_BUFFER);
if(path_element != NULL)
- path_element->buf = (uint8*)quote_string(cur_ancestor->keyname,
- key_special_chars);
+ {
+ /* XXX: Add command line option to choose output encoding */
+ regfi_interpret_keyname(f, cur_ancestor, REGFI_ENCODING_ASCII, true);
+ path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor);
+ }
+
if(path_element == NULL || path_element->buf == NULL
|| !void_stack_push(path_stack, path_element))
{
@@ -356,9 +352,9 @@ static void usage(void)
}
-bool removeRange(range_list* rl, uint32 offset, uint32 length)
+bool removeRange(range_list* rl, uint32_t offset, uint32_t length)
{
- int32 rm_idx;
+ int32_t rm_idx;
const range_list_element* cur_elem;
rm_idx = range_list_find(rl, offset);
@@ -418,7 +414,7 @@ int extractVKs(REGFI_FILE* f,
{
const range_list_element* cur_elem;
REGFI_VK_REC* vk;
- uint32 i, j;
+ uint32_t i, j;
for(i=0; i < range_list_size(unalloc_cells); i++)
{
@@ -455,76 +451,136 @@ int extractVKs(REGFI_FILE* f,
}
-int extractDataCells(REGFI_FILE* f,
+int extractDataCells(REGFI_FILE* file,
range_list* unalloc_cells,
range_list* unalloc_values)
{
const range_list_element* cur_elem;
REGFI_VK_REC* vk;
- const REGFI_HBIN* hbin;
+ range_list* bd_cells;
REGFI_BUFFER data;
- uint32 i, off, data_offset, data_maxsize;
+ uint32_t i, j, offset, cell_length, length;
+ int32_t max_size;
+ bool unalloc;
+
+ bd_cells = range_list_new();
+ if(bd_cells == NULL)
+ return 10;
+ data.buf = NULL;
+ data.len = 0;
for(i=0; i<range_list_size(unalloc_values); i++)
{
cur_elem = range_list_get(unalloc_values, i);
vk = (REGFI_VK_REC*)cur_elem->data;
if(vk == NULL)
- return 40;
+ return 11;
- if(vk->data_size == 0)
- vk->data = NULL;
- else
+ length = vk->data_size;
+ vk->data = NULL;
+ if(vk->data_size != 0)
{
- off = vk->data_off+REGFI_REGF_SIZE;
+ offset = vk->data_off+REGFI_REGF_SIZE;
if(vk->data_in_offset)
+ data = regfi_parse_little_data(file, vk->data_off,
+ length, false);
+ else
{
- data = regfi_load_data(f, vk->type, vk->data_off,
- vk->data_size, 4,
- vk->data_in_offset, false);
- vk->data = data.buf;
- vk->data_size = data.len;
- }
- else if(range_list_has_range(unalloc_cells, off, vk->data_size))
- {
- hbin = regfi_lookup_hbin(f, vk->data_off);
- if(hbin)
+ max_size = regfi_calc_maxsize(file, offset);
+ if(max_size >= 0
+ && regfi_parse_cell(file->fd, offset, NULL, 0,
+ &cell_length, &unalloc)
+ && (cell_length & 0x00000007) == 0
+ && cell_length <= max_size)
{
- data_offset = vk->data_off+REGFI_REGF_SIZE;
- data_maxsize = hbin->block_size + hbin->file_off - data_offset;
- data = regfi_load_data(f, vk->type, data_offset,
- vk->data_size, data_maxsize,
- vk->data_in_offset, false);
- vk->data = data.buf;
- vk->data_size = data.len;
-
- if(vk->data != NULL)
+ if(cell_length - 4 < length)
{
- /* XXX: The following may not make sense now in light of big data
- * records.
- */
- /* XXX: This strict checking prevents partial recovery of data
- * cells. Also, see code for regfi_load_data and note that
- * lengths indicated in VK records are sometimes just plain
- * wrong. Need a feedback mechanism to be more fuzzy with
- * data cell lengths and the ranges removed.
- *
- * The introduction of REGFI_BUFFER in regfi_load_data has
- * fixed some of this. Should review again with respect to
- * the other issues mentioned above though.
+ /* Multi-cell "big data" */
+
+ /* XXX: All big data records thus far have been 16 bytes long.
+ * Should we check for this precise size instead of just
+ * relying upon the above check?
*/
- /* A data record was recovered. Remove from unalloc_cells. */
- if(!removeRange(unalloc_cells, off, vk->data_size))
- return 50;
+ if (file->major_version >= 1 && file->minor_version >= 5)
+ {
+ /* Attempt to parse a big data record */
+ data = regfi_load_big_data(file, offset, length,
+ cell_length, bd_cells, false);
+
+ /* XXX: if this turns out NULL, should fall back to truncating cell */
+ if(data.buf != NULL)
+ {
+ for(j=0; j<range_list_size(bd_cells); j++)
+ {
+ cur_elem = range_list_get(bd_cells, j);
+ if(cur_elem == NULL)
+ return 20;
+ if(!range_list_has_range(unalloc_cells,
+ cur_elem->offset,
+ cur_elem->length))
+ {
+ fprintf(stderr,
+ "WARN: Successfully parsed big data at offset"
+ " 0x%.8X was rejected because some substructure"
+ " (offset=0x%.8X) is allocated or used in other"
+ " recovered structures.\n",
+ offset, cur_elem->offset);
+ talloc_free(data.buf);
+ data.buf = NULL;
+ data.len = 0;
+ break;
+ }
+ }
+
+ if(data.buf != NULL)
+ {
+ for(j=0; j<range_list_size(bd_cells); j++)
+ {
+ cur_elem = range_list_get(bd_cells, j);
+ if(cur_elem == NULL)
+ return 21;
+
+ if(!removeRange(unalloc_cells,
+ cur_elem->offset,
+ cur_elem->length))
+ { return 22; }
+ }
+ }
+ }
+
+ }
+ else
+ {
+ fprintf(stderr,
+ "WARN: Data length (0x%.8X)"
+ " larger than remaining cell length (0x%.8X)"
+ " while parsing data record at offset 0x%.8X."
+ " Truncating...\n",
+ length, cell_length - 4, offset);
+ length = cell_length - 4;
+ }
+ }
+
+ /* Typical 1-cell data */
+ if(range_list_has_range(unalloc_cells, offset, length))
+ {
+ data = regfi_parse_data(file, offset, length, false);
+ if(data.buf != NULL)
+ if(!removeRange(unalloc_cells, offset, length))
+ return 30;
}
}
- else
- vk->data = NULL;
}
+ /* XXX: Need to come up with a different way to link these so the
+ * vk->data item can be removed from the structure.
+ */
+ vk->data = regfi_buffer_to_data(data);
+ talloc_steal(vk, vk->data);
}
}
+ range_list_free(bd_cells);
return 0;
}
@@ -536,7 +592,7 @@ int extractKeys(REGFI_FILE* f,
{
const range_list_element* cur_elem;
REGFI_NK_REC* key;
- uint32 i, j;
+ uint32_t i, j;
int error_code = 0;
for(i=0; i < range_list_size(unalloc_cells); i++)
@@ -589,9 +645,9 @@ int extractValueLists(REGFI_FILE* f,
{
REGFI_NK_REC* nk;
REGFI_VK_REC* vk;
- const REGFI_HBIN* hbin;
const range_list_element* cur_elem;
- uint32 i, j, num_keys, off, values_length, max_length;
+ uint32_t i, j, num_keys, off, values_length;
+ int32_t max_size;
num_keys=range_list_size(unalloc_keys);
for(i=0; i<num_keys; i++)
@@ -603,14 +659,12 @@ int extractValueLists(REGFI_FILE* f,
if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
{
- hbin = regfi_lookup_hbin(f, nk->values_off);
-
- if(hbin != NULL)
+ off = nk->values_off + REGFI_REGF_SIZE;
+ max_size = regfi_calc_maxsize(f, off);
+ if(max_size >= 0)
{
- off = nk->values_off + REGFI_REGF_SIZE;
- max_length = hbin->block_size + hbin->file_off - off;
nk->values = regfi_load_valuelist(f, off, nk->num_values,
- max_length, false);
+ max_size, false);
if(nk->values != NULL && nk->values->elements != NULL)
{
/* Number of elements in the value list may be shorter than advertised
@@ -618,7 +672,7 @@ int extractValueLists(REGFI_FILE* f,
* only throw out the whole value list if it bleeds into an already
* parsed structure.
*/
- values_length = (nk->values->num_values+1)*sizeof(uint32);
+ values_length = (nk->values->num_values+1)*sizeof(uint32_t);
if(values_length != (values_length & 0xFFFFFFF8))
values_length = (values_length & 0xFFFFFFF8) + 8;
@@ -682,7 +736,7 @@ int extractSKs(REGFI_FILE* f,
{
const range_list_element* cur_elem;
REGFI_SK_REC* sk;
- uint32 i, j;
+ uint32_t i, j;
for(i=0; i < range_list_size(unalloc_cells); i++)
{
@@ -733,7 +787,7 @@ int main(int argc, char** argv)
char* tmp_path;
REGFI_NK_REC* tmp_key;
REGFI_VK_REC* tmp_value;
- uint32 argi, arge, i, j, ret, num_unalloc_keys;
+ uint32_t argi, arge, i, j, ret, num_unalloc_keys;
/* Process command line arguments */
if(argc < 2)
@@ -884,7 +938,10 @@ int main(int argc, char** argv)
printKey(f, tmp_key, parent_paths[i]);
if(tmp_key->num_values > 0 && tmp_key->values != NULL)
{
- tmp_name = quote_string(tmp_key->keyname, key_special_chars);
+ /* XXX: Add command line option to choose output encoding */
+ regfi_interpret_keyname(f, tmp_key, REGFI_ENCODING_ASCII, true);
+
+ tmp_name = get_quoted_keyname(tmp_key);
tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
if(tmp_path == NULL)
{
diff --git a/src/reglookup.c b/src/reglookup.c
index e9d2a2c..9969065 100644
--- a/src/reglookup.c
+++ b/src/reglookup.c
@@ -1,7 +1,8 @@
/*
* A utility to read a Windows NT and later registry files.
*
- * Copyright (C) 2005-2009 Timothy D. Morgan
+ * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
* Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
*
* This program is free software; you can redistribute it and/or modify
@@ -17,7 +18,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: reglookup.c 150 2009-03-02 02:17:46Z tim $
+ * $Id: reglookup.c 172 2010-03-08 03:04:34Z tim $
*/
@@ -30,6 +31,7 @@
#include "void_stack.h"
/* Globals, influenced by command line parameters */
+bool print_value_mtime = false;
bool print_verbose = false;
bool print_security = false;
bool print_header = true;
@@ -49,28 +51,18 @@ REGFI_FILE* f;
#include "common.c"
-void printValue(const REGFI_VK_REC* vk, char* prefix)
+void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
{
+ REGFI_DATA* data;
char* quoted_value = NULL;
char* quoted_name = NULL;
char* conv_error = NULL;
const char* str_type = NULL;
- uint32 size = vk->data_size;
-
- /* Microsoft's documentation indicates that "available memory" is
- * the limit on value sizes. Annoying. We limit it to 1M which
- * should rarely be exceeded, unless the file is corrupt or
- * malicious. For more info, see:
- * http://msdn2.microsoft.com/en-us/library/ms724872.aspx
- */
- if(size > REGFI_VK_MAX_DATA_LENGTH)
- {
- fprintf(stderr, "WARN: value data size %d larger than "
- "%d, truncating...\n", size, REGFI_VK_MAX_DATA_LENGTH);
- size = REGFI_VK_MAX_DATA_LENGTH;
- }
-
- quoted_name = quote_string(vk->valuename, key_special_chars);
+ char mtime[20];
+ time_t tmp_time[1];
+ struct tm* tmp_time_s = NULL;
+
+ quoted_name = get_quoted_valuename(vk);
if (quoted_name == NULL)
{ /* Value names are NULL when we're looking at the "(default)" value.
* Currently we just return a 0-length string to try an eliminate
@@ -83,16 +75,13 @@ void printValue(const REGFI_VK_REC* vk, char* prefix)
bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
quoted_name[0] = '\0';
}
+
+ data = regfi_iterator_fetch_data(iter, vk);
- if(vk->data == NULL)
- {
- if(print_verbose)
- fprintf(stderr, "INFO: While quoting value for '%s/%s', "
- "data pointer was NULL.\n", prefix, quoted_name);
- }
- else
+ printMsgs(iter->f);
+ if(data != NULL)
{
- quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
+ quoted_value = data_to_ascii(data, &conv_error);
if(quoted_value == NULL)
{
if(conv_error == NULL)
@@ -102,29 +91,39 @@ void printValue(const REGFI_VK_REC* vk, char* prefix)
fprintf(stderr, "WARN: Could not quote value for '%s/%s'. "
"Returned error: %s\n", prefix, quoted_name, conv_error);
}
- else if(conv_error != NULL && print_verbose)
- fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+ else if(conv_error != NULL)
+ fprintf(stderr, "WARN: While quoting value for '%s/%s', "
"warning returned: %s\n", prefix, quoted_name, conv_error);
+ regfi_free_data(data);
}
+ if(print_value_mtime)
+ {
+ *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime);
+ tmp_time_s = gmtime(tmp_time);
+ strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+ }
+ else
+ mtime[0] = '\0';
+
str_type = regfi_type_val2str(vk->type);
if(print_security)
{
if(str_type == NULL)
- printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
- vk->type, quoted_value);
+ printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name,
+ vk->type, quoted_value, mtime);
else
- printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
- str_type, quoted_value);
+ printf("%s/%s,%s,%s,%s,,,,\n", prefix, quoted_name,
+ str_type, quoted_value, mtime);
}
else
{
if(str_type == NULL)
- printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
- vk->type, quoted_value);
+ printf("%s/%s,0x%.8X,%s,%s\n", prefix, quoted_name,
+ vk->type, quoted_value, mtime);
else
- printf("%s/%s,%s,%s,\n", prefix, quoted_name,
- str_type, quoted_value);
+ printf("%s/%s,%s,%s,%s\n", prefix, quoted_name,
+ str_type, quoted_value, mtime);
}
if(quoted_value != NULL)
@@ -142,7 +141,7 @@ char** splitPath(const char* s)
const char* cur = s;
char* next = NULL;
char* copy;
- uint32 ret_cur = 0;
+ uint32_t ret_cur = 0;
ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
if (ret_val == NULL)
@@ -189,7 +188,7 @@ char** splitPath(const char* s)
void freePath(char** path)
{
- uint32 i;
+ uint32_t i;
if(path == NULL)
return;
@@ -205,14 +204,14 @@ void freePath(char** path)
char* iter2Path(REGFI_ITERATOR* i)
{
const REGFI_ITER_POSITION* cur;
- uint32 buf_left = 127;
- uint32 buf_len = buf_left+1;
- uint32 name_len = 0;
- uint32 grow_amt;
+ const REGFI_NK_REC* tmp_key;
+ uint32_t buf_left = 127;
+ uint32_t buf_len = buf_left+1;
+ uint32_t name_len = 0;
+ uint32_t grow_amt;
char* buf;
char* new_buf;
char* name;
- const char* cur_name;
void_stack_iterator* iter;
buf = (char*)malloc((buf_len)*sizeof(char));
@@ -240,17 +239,18 @@ char* iter2Path(REGFI_ITERATOR* i)
{
cur = void_stack_iterator_next(iter);
if (cur == NULL)
- cur_name = i->cur_key->keyname;
+ tmp_key = i->cur_key;
else
- cur_name = cur->nk->keyname;
+ tmp_key = cur->nk;
+
+ name = get_quoted_keyname(tmp_key);
buf[buf_len-buf_left-1] = '/';
buf_left -= 1;
- name = quote_string(cur_name, key_special_chars);
name_len = strlen(name);
if(name_len+1 > buf_left)
{
- grow_amt = (uint32)(buf_len/2);
+ grow_amt = (uint32_t)(buf_len/2);
buf_len += name_len+1+grow_amt-buf_left;
if((new_buf = realloc(buf, buf_len)) == NULL)
{
@@ -280,7 +280,7 @@ void printValueList(REGFI_ITERATOR* iter, char* prefix)
while(value != NULL)
{
if(!type_filter_enabled || (value->type == type_filter))
- printValue(value, prefix);
+ printValue(iter, value, prefix);
regfi_free_value(value);
value = regfi_iterator_next_value(iter);
printMsgs(iter->f);
@@ -296,14 +296,14 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
char* sacl = NULL;
char* dacl = NULL;
char* quoted_classname;
- char* error_msg = NULL;
char mtime[20];
time_t tmp_time[1];
struct tm* tmp_time_s = NULL;
const REGFI_SK_REC* sk;
const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
+ REGFI_CLASSNAME* classname;
- *tmp_time = nt_time_to_unix(&k->mtime);
+ *tmp_time = regfi_nt2unix_time(&k->mtime);
tmp_time_s = gmtime(tmp_time);
strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
@@ -322,32 +322,31 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
if(dacl == NULL)
dacl = empty_str;
- if(k->classname != NULL)
+ classname = regfi_iterator_fetch_classname(iter, k);
+ printMsgs(iter->f);
+ if(classname != NULL)
{
- quoted_classname = quote_unicode((uint8*)k->classname, k->classname_length,
- key_special_chars, &error_msg);
- if(quoted_classname == NULL)
+ if(classname->interpreted == NULL)
{
- if(error_msg == NULL)
- fprintf(stderr, "ERROR: Could not quote classname"
- " for key '%s' due to unknown error.\n", full_path);
- else
- {
- fprintf(stderr, "ERROR: Could not quote classname"
- " for key '%s' due to error: %s\n", full_path, error_msg);
- free(error_msg);
- }
+ fprintf(stderr, "WARN: Could not convert class name"
+ " charset for key '%s'. Quoting raw...\n", full_path);
+ quoted_classname = quote_buffer(classname->raw, classname->size,
+ key_special_chars);
}
- else if (error_msg != NULL)
+ else
+ quoted_classname = quote_string(classname->interpreted,
+ key_special_chars);
+
+ if(quoted_classname == NULL)
{
- if(print_verbose)
- fprintf(stderr, "INFO: While converting classname"
- " for key '%s': %s.\n", full_path, error_msg);
- free(error_msg);
+ fprintf(stderr, "ERROR: Could not quote classname"
+ " for key '%s' due to unknown error.\n", full_path);
+ quoted_classname = empty_str;
}
}
else
quoted_classname = empty_str;
+ regfi_free_classname(classname);
printMsgs(iter->f);
printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime,
@@ -460,7 +459,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
REGFI_VK_REC* value;
char* tmp_path_joined;
const char** tmp_path;
- uint32 i;
+ uint32_t i;
if(path == NULL)
return -1;
@@ -510,7 +509,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
if(!type_filter_enabled || (value->type == type_filter))
- printValue(value, tmp_path_joined);
+ printValue(iter, value, tmp_path_joined);
regfi_free_value(value);
free(tmp_path);
@@ -554,6 +553,7 @@ static void usage(void)
fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
+ fprintf(stderr, "\t-i\t includes parent key modification times with child values.\n");
fprintf(stderr, "\n");
}
@@ -563,7 +563,7 @@ int main(int argc, char** argv)
char** path = NULL;
REGFI_ITERATOR* iter;
int retr_path_ret;
- uint32 argi, arge;
+ uint32_t argi, arge;
/* Process command line arguments */
if(argc < 2)
@@ -611,6 +611,8 @@ int main(int argc, char** argv)
print_security = false;
else if (strcmp("-v", argv[argi]) == 0)
print_verbose = true;
+ else if (strcmp("-i", argv[argi]) == 0)
+ print_value_mtime = true;
else
{
usage();
@@ -631,9 +633,13 @@ int main(int argc, char** argv)
if(print_verbose)
regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
- iter = regfi_iterator_new(f);
+ /* XXX: add command line option to choose output encoding */
+ iter = regfi_iterator_new(f, REGFI_ENCODING_ASCII);
if(iter == NULL)
+ {
+ printMsgs(f);
bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
+ }
if(print_header)
{
--
debian-forensics/reglookup
More information about the forensics-changes
mailing list