reglookup+git/python/experimental/regfi/pyregfi.h000664 001750 001750 00000012625 12566244653 023441 0ustar00sophiesophie000000 000000 /* * Top-level definitions for pyregfi to be processed by Michael Cohen's * automated Python bindings generator. * * Copyright (C) 2010 Michael I. Cohen * Copyright (C) 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 * 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: $ * */ #ifndef PYREGFI_H_ # define PYREGFI_H_ #include "class.h" #include "aff4_errors.h" #include "regfi.h" /** Forward declarations */ struct RegistryFile_t; struct RegistryKey_t; struct SubkeyIterator_t; struct ValueIterator_t; struct TreeIterator_t; BIND_STRUCT(REGFI_NK) BIND_STRUCT(REGFI_VK) BIND_STRUCT(REGFI_DATA) /** This is the base class for data objects */ CLASS(RawData, Object) const REGFI_DATA *data; const REGFI_VK *rec; RawData METHOD(RawData, Con, REGFI_DATA *data, REGFI_VK *record); /** Return the raw buffer as a string. By default we only return this much data - specify a required length to return more. DEFAULT(len) = 4096; */ int METHOD(RawData, get_value, OUT char *buffer, int len); END_CLASS CLASS(DataString, RawData) BORROWED char *METHOD(DataString, get_value); END_CLASS CLASS(DWORDData, RawData) uint64_t METHOD(DWORDData, get_value); END_CLASS /** This is an iterator for traversing an entire registry hive */ CLASS(TreeIterator, Object) PRIVATE REGFI_ITERATOR *iter; PRIVATE struct RegistryFile_t *file; PRIVATE bool root_traversed; struct TreeIterator_t *METHOD(TreeIterator, Con, struct RegistryFile_t *file, char **path, REGFI_ENCODING encoding); void METHOD(TreeIterator, __iter__); struct RegistryKey_t *METHOD(TreeIterator, iternext); int METHOD(TreeIterator, down); int METHOD(TreeIterator, up); struct RegistryKey_t *METHOD(TreeIterator, current); int METHOD(TreeIterator, to_root); END_CLASS /** XXX */ CLASS(RegistryKey, Object) struct RegistryFile_t *file; const REGFI_NK *key; struct RegistryKey_t *METHOD(RegistryKey, Con, struct RegistryFile_t *file, REGFI_NK *base_key); struct SubkeyIterator_t *METHOD(RegistryKey, subkeys); struct ValueIterator_t *METHOD(RegistryKey, values); END_CLASS /** This is an iterator for reading keys from the registry */ CLASS(SubkeyIterator, Object) struct RegistryFile_t *file; PRIVATE const REGFI_SUBKEY_LIST *list; PRIVATE uint32_t cur; SubkeyIterator METHOD(SubkeyIterator, Con, struct RegistryFile_t *file, REGFI_NK *key); void METHOD(SubkeyIterator, __iter__); RegistryKey METHOD(SubkeyIterator, iternext); END_CLASS /** This is an iterator for reading values from the registry */ CLASS(ValueIterator, Object) struct RegistryFile_t *file; PRIVATE const REGFI_VALUE_LIST *list; PRIVATE uint32_t cur; ValueIterator METHOD(ValueIterator, Con, struct RegistryFile_t *file, REGFI_NK *key); void METHOD(ValueIterator, __iter__); REGFI_VK *METHOD(ValueIterator, iternext); END_CLASS CLASS(RegistryFile, Object) REGFI_FILE *reg; int fd; RegistryFile METHOD(RegistryFile, Con, char *filename); /* Get an iterator for a specific path in the register if path is specified. XXX: can we switch to UTF-8 and have Python properly import that? DEFAULT(path) == NULL; DEFAULT(encoding) = REGFI_ENCODING_ASCII; */ TreeIterator METHOD(RegistryFile, TreeIterator, char **path, REGFI_ENCODING encoding); /** Set the verbosity level of messages generated by the library for the * current thread. * * @param mask An integer representing the types of messages desired. * Acceptable values are created through bitwise ORs of * REGFI_LOG_* values. For instance, if only errors and * informational messages were desired (but not warnings), * then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO * By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN. * * @return true on success and false on failure. Failure occurs if * underlying pthread functions fail. errno is set in this case. * * Message masks are set in a thread-specific way. If one were to set a message * mask in one thread and then spawn a new thread, then the new thread will have * it's message mask reset to the default. This function may be called at any * time and will take effect immediately for the current thread. * * @note When a non-zero message mask is set, messages will * accumulate in memory without limit if they are not fetched using * @ref regfi_get_log_str and subsequently freed by the caller. It is * recommended that messsages be fetched after each regfi API call in * order to provide the most context. * * @ingroup regfiBase */ int METHOD(RegistryFile, set_log_mask, uint16_t mask); END_CLASS void pyregfi_init(); #endif /* !PYREGFI_H_ */ reglookup+git/python/pyregfi/000775 001750 001750 00000000000 12566244653 017471 5ustar00sophiesophie000000 000000 reglookup+git/python/experimental/include/class.h000664 001750 001750 00000034467 12566244653 023440 0ustar00sophiesophie000000 000000 /*************************************************** Classes and objects in C This file makes it easy to implement classes and objects in C. To define a class we need to perform three steps: Define the class prototype. This is suitable to go in a .h file for general use by other code. Note all classes extend Object. Example:: CLASS(Foo, Object) int x; int y; //This declares a method of a class Foo, called Con returning a //Foo object. In other words it is a constructor. Foo METHOD(Foo, Con, int x, int y); int METHOD(Foo, add); END_CLASS Now we need to define some functions for the constructor and methods. Note that the constuctor is using ALLOCATE_CLASS to allocate space for the class structures. Callers may call with self==NULL to force allocation of a new class. Note that we do not call the constructor of our superclass implicitly here. (Calling the sperclass constructor is optional, but ALLOCATE_CLASS is not.). Foo Foo_Con(Foo self,int x,int y) { self->x = x; self->y = y; return self; }; int Foo_add(Foo this) { return (this->x + this->y); }; Now we need to define the Virtual function table - These are those functions and attributes which are defined in this class (over its superclass). Basically these are all those things in the class definition above, with real function names binding them. (Note that by convention we preceed the name of the method with the name of the class): VIRTUAL(Foo,Object) VMETHOD(Con) = Foo_Con; VMETHOD(add) = Foo_add; END_VIRTUAL We can use inheritance too: CLASS(Bar, Foo) Bar METHOD(Bar, Con, char *something) END_CLASS Here Bar extends Foo and defines a new constructor with a different prototype: VIRTUAL(Bar,Foo) VMETHOD(Con) = Bar_Con END_VIRTUAL If there is a function which expects a Foo, we will need to over ride the Foo constructor in the Bar, so the function will not see the difference between the Foo and Bar: CLASS(Bar,Foo) int bar_attr; END_CLASS Foo Bar_Con(Foo self, int x, int y) { ... } VIRTUAL(Bar, Foo) VMETHOD(super.Con) = Bar_Con END_VIRTUAL Note that in this case we are over riding the Con method defined in Foo while creating derived Bar classes. The notation in the VIRTUAL table is to use super.Con, because Foo's Con method (the one we are over riding), can be located by using super.Con inside a Bar object. Imagine now that in Bar_Con we wish to use methods and attributes defined in Bar. Since Bar_Con over rides Bar's base class (Foo) it must have the prototype described above. Since self is of type Foo its impossible to use self->bar_attr (There is no bar_attr in Foo - its in Bar). In this case, we need to make a type cast to convice C that self is actually a Bar not a Foo: Foo Bar_Con(Foo self, int x, int y) { Bar this = (Bar)self; this->bar_attr=1 }; This allows us to access bars attributes. This is a general oddity with C style classes, which C++ and Java hide. In C we must always know which class defines which method and attribute and reference the right class's method. So for example if we want to call a Bar's add method: Bar a; a->super.add() because add is defined in Bar's super class (Foo). Constract this with C++ or Java which hide where methods are defined and simply make all methods appear like they were defined inside the derived class. This takes a while to get used to but the compiler will ensure that the references are correct - otherwise things will generally not compile properly. This difference can be used for good and bad. It is possible in C to call the base class's version of the method at any time (despite the fact it was over ridden). For example: CLASS(Derived, Foo) int METHOD(Derived, add); END_CLASS VIRTUAL(Derived, Foo) VMETHOD(add) = Derived_add END_VIRTUAL If d is a Derived object, we can call Foo's version like this: d->super.add() But Derived's version is accessed by: d->add() Sometimes a derived class may want to over ride the base class's methods as well, in this case the VIRTUAL section should over ride super.add as well. */ /****************************************************** # Copyright 2004: Commonwealth of Australia. # # Developed by the Computer Network Vulnerability Team, # Information Security Group. # Department of Defence. # # Michael Cohen # # ****************************************************** # Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$ # ****************************************************** # # * This program is free software; you can redistribute it and/or # * modify it under the terms of the GNU General Public License # * as published by the Free Software Foundation; either version 2 # * of the License, or (at your option) any later version. # * # * This program is distributed in the hope that it will be useful, # * but WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # * GNU General Public License for more details. # * # * You should have received a copy of the GNU General Public License # * along with this program; if not, write to the Free Software # * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ******************************************************/ #ifndef __CLASS_H__ #define __CLASS_H__ #ifdef __cplusplus extern "C" { #endif #ifdef min #undef min #endif #define min(X, Y) ((X) < (Y) ? (X) : (Y)) #ifdef max #undef max #endif #define max(X, Y) ((X) > (Y) ? (X) : (Y)) #include #define CLASS(class,super_class) \ typedef struct class ## _t *class; \ int class ## _init(Object self); \ extern struct class ## _t __ ## class; \ struct class ## _t { struct super_class ## _t super; \ class __class__; \ super_class __super__; #define METHOD(cls, name, ... ) \ (* name)(cls self, ## __VA_ARGS__ ) // Class methods are attached to the class but are not called with // an instance. This is similar to the python class method or java // static methods. #define CLASS_METHOD(name, ... ) \ (*name)(__VA_ARGS__) /*************************************************** This is a convenience macro which may be used if x if really large ***************************************************/ #define CALL(x, method, ... ) \ (x)->method((x), ## __VA_ARGS__) #define END_CLASS }; /*************************************************** This is used to set the classes up for use: class_init = checks the class template (__class) to see if it has been allocated. otherwise allocates it in the global context. class_Alloc = Allocates new memory for an instance of the class. This is a recursive function calling each super class in turn and setting the currently over ridden defaults. So for eample suppose this class (foo) derives from bar, we first fill the template with bars methods, and attributes. Then we over write those with foos methods and attributes. **********************************************************/ #define VIRTUAL(class,superclass) \ struct class ## _t __ ## class; \ \ int class ## _init(Object this) { \ class self = (class)this; \ if(self->__super__) return 1; \ superclass ##_init(this); \ this->__class__ = (Object)&__ ## class; \ self->__class__ = (class)&__ ## class; \ this->__super__ = (Object)&__ ## superclass; \ self->__super__ = (superclass)&__ ## superclass; \ this->__size = sizeof(struct class ## _t); \ this->__name__ = #class; #define SET_DOCSTRING(string) \ ((Object)self)->__doc__ = string #define END_VIRTUAL return 1; } #define VMETHOD(method) \ (self)->method #define VMETHOD_BASE(base, method) \ (((base)self)->method) #define CLASS_ATTR(self, base, method) \ (((base)self)->method) #define VATTR(attribute) \ (self)->attribute #define NAMEOF(obj) \ ((Object)obj)->__name__ #define SIZEOF(obj) \ ((Object)obj)->__size #define DOCSTRING(obj) \ ((Object)obj)->__doc__ #define INIT_CLASS(class) \ class ## _init((Object)&__ ## class) /************************************************************* This MACRO is used to construct a new Class using a constructor. This is done to try and hide the bare (unbound) method names in order to prevent name space pollution. (Bare methods may be defined as static within the implementation file). This macro ensures that class structures are initialised properly before calling their constructors. We require the following args: class - the type of class to make virt_class - The class where the method was defined constructors - The constructor method to use context - a talloc context to use. Note that the class and virt_class do not have to be the same if the method was not defined in the current class. For example suppose Foo extends Bar, but method is defined in Bar but inherited in Foo: CONSTRUCT(Foo, Bar, super.method, context) virt_class is Bar because thats where method was defined. *************************************************************/ // The following only initialises the class if the __super__ element // is NULL. This is fast as it wont call the initaliser unnecessaily #define CONSTRUCT(class, virt_class, constructor, context, ... ) \ (class)( __## class.__super__ == NULL ? \ class ## _init((Object)&__ ## class) : 0, \ __## virt_class.__super__ == NULL ? \ virt_class ## _init((Object)&__ ## virt_class): 0, \ ((virt_class)(&__ ## class))->constructor( \ (virt_class)_talloc_memdup(context, &__ ## class, sizeof(struct class ## _t), __location__ "(" #class ")"), \ ## __VA_ARGS__) ) /** This variant is useful when all we have is a class reference (GETCLASS(Foo)) or &__Foo */ #define CONSTRUCT_FROM_REFERENCE(class, constructor, context, ... ) \ ( (class)->constructor( \ (void *)_talloc_memdup(context, ((Object)class), ((Object)class)->__size, __location__ "(" #class "." #constructor ")"), \ ## __VA_ARGS__) ) /** Finds the size of the class in x */ #define CLASS_SIZE(class) \ ((Object)class)->__size typedef struct Object_t *Object; struct Object_t { //A reference to a class instance - this is useful to be able to //tell which class an object really belongs to: Object __class__; //And its super class: Object __super__; char *__name__; /** Objects may have a doc string associated with them. */ char *__doc__; //How large the class is: int __size; }; #define SUPER(base, imp, method, ...) \ ((base)&__ ## imp)->method((base)self, ## __VA_ARGS__) #define GETCLASS(class) \ (Object)&__ ## class // Returns true if the obj belongs to the class #define ISINSTANCE(obj,class) \ (((Object)obj)->__class__ == GETCLASS(class)) // This is a string comparison version of ISINSTANCE which works // across different shared objects. #define ISNAMEINSTANCE(obj, class) \ (obj && !strcmp(class, NAMEOF(obj))) // We need to ensure that class was properly initialised: #define ISSUBCLASS(obj,class) \ issubclass((Object)obj, (Object)&__ ## class) #define CLASSOF(obj) \ ((Object)obj)->__class__ void Object_init(Object); extern struct Object_t __Object; int issubclass(Object obj, Object class); extern void unimplemented(Object self); #define UNIMPLEMENTED(class, method) \ ((class)self)->method = (void *)unimplemented; #define ZSTRING_NO_NULL(str) str , (strlen(str)) #define ZSTRING(str) str , (strlen(str)+1) // These dont do anything but are useful to indicate when a function // parameter is used purely to return a value. They are now used to // assist the python binding generator in generating the right sort // of code #define OUT #define IN // This modifier before a class means that the class is abstract and // does not have an implementation - we do not generate bindings for // that class then. #define ABSTRACT // This modifier indicates that the following pointer is pointing to // a borrowed reference - callers must not free the memory after use. #define BORROWED // This tells the autobinder to generated bindings to this struct #define BOUND // This tells the autobinder to ignore this class as it should be // private to the implementation - external callers should not // access this. #define PRIVATE // This attribute of a method means that this method is a // desctructor - the object is no longer valid after this method is // run #define DESTRUCTOR // including this after an argument definition will cause the // autogenerator to assign default values to that parameter and make // it optional #define DEFAULT(x) // This explicitely denote that the type is a null terminated char // ptr as opposed to a pointer to char and length. #define ZString char * /* The following is a direction for the autogenerator to proxy the given class. This is done in the following way: 1) a new python type is created called Proxy_class_name() with a constructor which takes a surrogate object. 2) The proxy class contains a member "base" of the type of the proxied C class. 3) The returned python object may be passed to any C functions which expect the proxied class, and internal C calls will be converted to python method calls on the proxied object. */ #define PROXY_CLASS(name) /* This signals the autogenerator to bind the named struct */ #define BIND_STRUCT(name) // This means that the memory owned by this pointer is managed // externally (not using talloc). It is dangerous to use this // keyword too much because we are unable to manage its memory // appropriately and it can be free'd from under us. #define FOREIGN #endif #ifdef __cplusplus } /* closing brace for extern "C" */ #endif reglookup+git/python/pyregfi/winsec.py000664 001750 001750 00000015247 12566244653 021344 0ustar00sophiesophie000000 000000 #!/usr/bin/env python # Copyright (C) 2011 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 # 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: $ ## @package pyregfi.winsec # Low-level data structures for winsec library # import sys import os import uuid import ctypes import ctypes.util from ctypes import * from .structures import regfi is_win32 = hasattr(ctypes, 'windll') WINSEC_MAX_SUBAUTHS = 15 if is_win32: libc = cdll.msvcrt else: libc = cdll.LoadLibrary("libc.so.6") class WINSEC_UUID(Structure): pass class WINSEC_DOM_SID(Structure): pass class WINSEC_ACE(Structure): pass class WINSEC_ACL(Structure): pass class WINSEC_DESC(Structure): pass WINSEC_UUID._fields_ = [('time_low', c_uint32), ('time_mid', c_uint16), ('time_hi_and_version', c_uint16), ('clock_seq', c_uint8*2), ('node', c_uint8*6), ] WINSEC_DOM_SID._fields_ = [('sid_rev_num', c_uint8), ('num_auths', c_uint8), ('id_auths', c_uint8*6), ('sub_auths', c_uint32*WINSEC_MAX_SUBAUTHS), ] WINSEC_ACE._fields_ = [('type', c_uint8), ('flags', c_uint8), ('size', c_uint16), ('access_mask', c_uint32), ('obj_flags', c_uint32), ('obj_guid', POINTER(WINSEC_UUID)), ('inh_guid', POINTER(WINSEC_UUID)), ('trustee', POINTER(WINSEC_DOM_SID)), ] WINSEC_ACL._fields_ = [('revision', c_uint16), ('size', c_uint16), ('num_aces', c_uint32), ('aces', POINTER(POINTER(WINSEC_ACE))), ] WINSEC_DESC._fields_ = [('revision', c_uint8), ('sbz1', c_uint8), ('control', c_uint16), ('off_owner_sid', c_uint32), ('off_grp_sid', c_uint32), ('off_sacl', c_uint32), ('off_dacl', c_uint32), ('owner_sid', POINTER(WINSEC_DOM_SID)), ('grp_sid', POINTER(WINSEC_DOM_SID)), ('sacl', POINTER(WINSEC_ACL)), ('dacl', POINTER(WINSEC_ACL)), ] regfi.winsec_sid2str.argtypes = [POINTER(WINSEC_DOM_SID)] regfi.winsec_sid2str.restype = POINTER(c_char) def _guid2uuid(guid): if not guid: return None return uuid.UUID(fields=(guid.contents.time_low, guid.contents.time_mid, guid.contents.time_hi_and_version, guid.contents.clock_seq[0], guid.contents.clock_seq[1], guid.contents.node[0]<<40 ^ guid.contents.node[1]<<32 ^ guid.contents.node[2]<<24 ^ guid.contents.node[3]<<16 ^ guid.contents.node[4]<<8 ^ guid.contents.node[5])) ## Represents a Microsoft access control entry, which are elements of access # control lists. For more information, see: # http://msdn.microsoft.com/en-us/library/aa374868%28v=vs.85%29.aspx # # @note # This interface is subject to change class ACE(object): ## The type of entry as an integer type = 1234 ## The flags as an integer flags = 0x1234 ## The access mask/permissions as an integer access_mask = 0x1234 ## The trustee's SID as a string trustee = "S-1-2..." ## The object GUID as a Python UUID # May be None object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) ## The inherited object GUID as a Python UUID # May be None inherited_object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) def __init__(self, ace): # Just copy all of the values out so we don't need to manage memory self.object = _guid2uuid(ace.obj_guid) self.inherited_object = _guid2uuid(ace.inh_guid) c_str = regfi.winsec_sid2str(ace.trustee) self.trustee = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace') libc.free(c_str) self.type = int(ace.type) self.flags = int(ace.flags) self.access_mask = int(ace.access_mask) ## A Microsoft security descriptor # For more information, see: # http://msdn.microsoft.com/en-us/library/aa379563%28v=vs.85%29.aspx # class SecurityDescriptor(object): ## The security descriptor's owner SID, as a string owner = "S-1-2-..." ## The security descriptor's group SID, as a string group = "S-1-2-..." ## The system access control list represented as a list of @ref ACE objects. # # Is set to None if a sacl isn't defined sacl = [] ## The discretionary access control list represented as a list of @ref ACE objects # # Is set to None if a dacl isn't defined dacl = [] def __init__(self, sec_desc): c_str = regfi.winsec_sid2str(sec_desc.owner_sid) self.owner = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace') libc.free(c_str) c_str = regfi.winsec_sid2str(sec_desc.grp_sid) self.group = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace') libc.free(c_str) self.sacl = None if sec_desc.sacl: self.sacl = [] for i in range(0,sec_desc.sacl.contents.num_aces): self.sacl.append(ACE(sec_desc.sacl.contents.aces[i].contents)) self.dacl = None if sec_desc.dacl: self.dacl = [] for i in range(0,sec_desc.dacl.contents.num_aces): self.dacl.append(ACE(sec_desc.dacl.contents.aces[i].contents)) # Free class objects used for documentation del ACE.type,ACE.flags,ACE.access_mask,ACE.object,ACE.inherited_object del SecurityDescriptor.owner,SecurityDescriptor.group,SecurityDescriptor.sacl,SecurityDescriptor.dacl reglookup+git/000755 001750 001750 00000000000 12572265350 014473 5ustar00sophiesophie000000 000000 reglookup+git/src/reglookup.c000664 001750 001750 00000043342 12566244653 017453 0ustar00sophiesophie000000 000000 /* * A utility to read a Windows NT and later registry files. * * Copyright (C) 2005-2011 Timothy D. Morgan * Copyright (C) 2010 Tobias Mueller (portions of '-i' code) * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com * * 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$ */ #include #include #include #include #include #include "regfi.h" #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; bool path_filter_enabled = false; bool type_filter_enabled = false; char* path_filter = NULL; int type_filter; const char* registry_file = NULL; /* Other globals */ REGFI_FILE* f; /* XXX: A hack to share some functions with reglookup-recover.c. * Should move these into a proper library at some point. */ #include "common.c" static bool keysEqual(const REGFI_NK* x, const REGFI_NK* y) { return (x != NULL && y != NULL && x->offset == y->offset); } void printValue(REGFI_ITERATOR* iter, const REGFI_VK* vk, char* prefix) { const REGFI_NK* cur_key; const REGFI_DATA* data; char* quoted_value = NULL; char* quoted_name = NULL; char* conv_error = NULL; const char* str_type = NULL; 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 * ambiguity with a literal "(default)" value. The data type of a line * in the output allows one to differentiate between the parent key and * this value. */ quoted_name = malloc(1*sizeof(char)); if(quoted_name == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); quoted_name[0] = '\0'; } data = regfi_fetch_data(iter->f, vk); printMsgs(iter->f); if(data != NULL) { quoted_value = data_to_ascii(data, &conv_error); if(quoted_value == NULL) { if(conv_error == NULL) fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Memory allocation failure likely.\n", prefix, quoted_name); else fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Returned error: %s\n", prefix, quoted_name, conv_error); } 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_record(iter->f, data); } if(print_value_mtime) { cur_key = regfi_iterator_cur_key(iter); *tmp_time = regfi_nt2unix_time(cur_key->mtime); tmp_time_s = gmtime(tmp_time); strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s); regfi_free_record(iter->f, cur_key); } else mtime[0] = '\0'; str_type = regfi_type_val2str(vk->type); if(print_security) { if(str_type == NULL) printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name, vk->type, quoted_value, mtime); else 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,%s\n", prefix, quoted_name, vk->type, quoted_value, mtime); else printf("%s/%s,%s,%s,%s\n", prefix, quoted_name, str_type, quoted_value, mtime); } if(quoted_value != NULL) free(quoted_value); if(quoted_name != NULL) free(quoted_name); if(conv_error != NULL) free(conv_error); } char** splitPath(const char* s) { char** ret_val; const char* cur = s; char* next = NULL; char* copy; uint32_t ret_cur = 0; ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**)); if (ret_val == NULL) return NULL; ret_val[0] = NULL; /* We return a well-formed, 0-length, path even when input is icky. */ if (s == NULL) return ret_val; while((next = strchr(cur, '/')) != NULL) { if ((next-cur) > 0) { copy = (char*)malloc((next-cur+1)*sizeof(char)); if(copy == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n"); memcpy(copy, cur, next-cur); copy[next-cur] = '\0'; ret_val[ret_cur++] = copy; if(ret_cur < (REGFI_MAX_DEPTH+1+1)) ret_val[ret_cur] = NULL; else bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n"); } cur = next+1; } /* Grab last element, if path doesn't end in '/'. */ if(strlen(cur) > 0) { copy = strdup(cur); ret_val[ret_cur++] = copy; if(ret_cur < (REGFI_MAX_DEPTH+1+1)) ret_val[ret_cur] = NULL; else bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n"); } return ret_val; } void freePath(char** path) { uint32_t i; if(path == NULL) return; for(i=0; path[i] != NULL; i++) free(path[i]); free(path); } /* Returns a quoted path of the current iterator's position */ char* iter2Path(REGFI_ITERATOR* i) { const REGFI_NK** path; uint32_t k; 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; buf = (char*)malloc((buf_len)*sizeof(char)); if (buf == NULL) return NULL; buf[0] = '\0'; path = regfi_iterator_ancestry(i); if(path == NULL) { free(buf); return NULL; } for(k=0; path[k] != NULL; k++) { /* skip root element's name */ if(k == 0) { buf[0] = '/'; buf[1] = '\0'; } else { name = get_quoted_keyname(path[k]); buf[buf_len-buf_left-1] = '/'; buf_left -= 1; name_len = strlen(name); if(name_len+1 > buf_left) { grow_amt = (uint32_t)(buf_len/2); buf_len += name_len+1+grow_amt-buf_left; if((new_buf = realloc(buf, buf_len)) == NULL) { regfi_free_record(i->f, path); free(name); free(buf); return NULL; } buf = new_buf; buf_left = grow_amt + name_len + 1; } strncpy(buf+(buf_len-buf_left-1), name, name_len); buf_left -= name_len; buf[buf_len-buf_left-1] = '\0'; free(name); } } regfi_free_record(i->f, path); return buf; } void printValueList(REGFI_ITERATOR* iter, char* prefix) { const REGFI_VK* value; regfi_iterator_first_value(iter); while((value = regfi_iterator_cur_value(iter)) != NULL) { if(!type_filter_enabled || (value->type == type_filter)) printValue(iter, value, prefix); regfi_free_record(iter->f, value); regfi_iterator_next_value(iter); printMsgs(iter->f); } } void printKey(REGFI_ITERATOR* iter, char* full_path) { static char empty_str[1] = ""; char* owner = NULL; char* group = NULL; char* sacl = NULL; char* dacl = NULL; char mtime[24]; char* quoted_classname; const REGFI_SK* sk; const REGFI_NK* key = regfi_iterator_cur_key(iter); const REGFI_CLASSNAME* classname; formatTime(key->mtime, mtime); if(print_security && (sk=regfi_fetch_sk(iter->f, key))) { owner = regfi_get_owner(sk->sec_desc); group = regfi_get_group(sk->sec_desc); sacl = regfi_get_sacl(sk->sec_desc); dacl = regfi_get_dacl(sk->sec_desc); regfi_free_record(iter->f, sk); if(owner == NULL) owner = empty_str; if(group == NULL) group = empty_str; if(sacl == NULL) sacl = empty_str; if(dacl == NULL) dacl = empty_str; classname = regfi_fetch_classname(iter->f, key); printMsgs(iter->f); if(classname != NULL) { if(classname->interpreted == NULL) { 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 quoted_classname = quote_string(classname->interpreted, key_special_chars); if(quoted_classname == NULL) { 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_record(iter->f, classname); printMsgs(iter->f); printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, owner, group, sacl, dacl, quoted_classname); if(owner != empty_str) free(owner); if(group != empty_str) free(group); if(sacl != empty_str) free(sacl); if(dacl != empty_str) free(dacl); if(quoted_classname != empty_str) free(quoted_classname); } else printf("%s,KEY,,%s\n", full_path, mtime); regfi_free_record(iter->f, key); } void printKeyTree(REGFI_ITERATOR* iter) { const REGFI_NK* root = NULL; const REGFI_NK* cur = NULL; const REGFI_NK* sub = NULL; char* path = NULL; int key_type = regfi_type_str2val("KEY"); bool print_this = true; root = regfi_iterator_cur_key(iter); cur = root = regfi_reference_record(iter->f, root); regfi_iterator_first_subkey(iter); sub = regfi_iterator_cur_subkey(iter); printMsgs(iter->f); if(root == NULL) bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n"); do { if(print_this) { path = iter2Path(iter); if(path == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n"); if(!type_filter_enabled || (key_type == type_filter)) printKey(iter, path); if(!type_filter_enabled || (key_type != type_filter)) printValueList(iter, path); free(path); } if(sub == NULL) { if(!keysEqual(cur, root)) { regfi_free_record(iter->f, cur); cur = NULL; /* We're done with this sub-tree, going up and hitting other branches. */ if(!regfi_iterator_up(iter)) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n"); } cur = regfi_iterator_cur_key(iter); if(cur == NULL) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n"); } regfi_iterator_next_subkey(iter); sub = regfi_iterator_cur_subkey(iter); } print_this = false; } else { /* We have unexplored sub-keys. * Let's move down and print this first sub-tree out. */ regfi_free_record(iter->f, cur); cur = NULL; if(!regfi_iterator_down(iter)) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n"); } cur = regfi_iterator_cur_key(iter); regfi_free_record(iter->f, sub); regfi_iterator_first_subkey(iter); sub = regfi_iterator_cur_subkey(iter); print_this = true; } printMsgs(iter->f); } while(!(keysEqual(cur, root) && (sub == NULL))); if(cur != NULL) regfi_free_record(iter->f, cur); regfi_free_record(iter->f, root); if(print_verbose) fprintf(stderr, "INFO: Finished printing key tree.\n"); } /* XXX: What if there is BOTH a value AND a key with that name?? * What if there are multiple keys/values with the same name?? */ /* * Returns 0 if path was not found. * Returns 1 if path was found as value. * Returns 2 if path was found as key. * Returns less than 0 on other error. */ int retrievePath(REGFI_ITERATOR* iter, char** path) { const REGFI_VK* value; char* tmp_path_joined; const char** tmp_path; uint32_t i; if(path == NULL) return -1; /* One extra for any value at the end, and one more for NULL */ tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1)); if(tmp_path == NULL) return -2; /* Strip any potential value name at end of path */ for(i=0; (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1); i++) { tmp_path[i] = path[i]; } tmp_path[i] = NULL; if(print_verbose) fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n", path_filter); /* Special check for '/' path filter */ if(path[0] == NULL) { if(print_verbose) fprintf(stderr, "INFO: Found final path element as root key.\n"); free(tmp_path); return 2; } if(!regfi_iterator_descend(iter, tmp_path)) { printMsgs(iter->f); free(tmp_path); return 0; } if(regfi_iterator_find_value(iter, path[i])) { if(print_verbose) fprintf(stderr, "INFO: Found final path element as value.\n"); value = regfi_iterator_cur_value(iter); printMsgs(iter->f); tmp_path_joined = iter2Path(iter); if((value == NULL) || (tmp_path_joined == NULL)) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n"); if(!type_filter_enabled || (value->type == type_filter)) printValue(iter, value, tmp_path_joined); regfi_free_record(iter->f, value); free(tmp_path); free(tmp_path_joined); return 1; } else if(regfi_iterator_find_subkey(iter, path[i])) { printMsgs(iter->f); if(print_verbose) fprintf(stderr, "INFO: Found final path element as key.\n"); if(!regfi_iterator_down(iter)) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n"); } return 2; } printMsgs(iter->f); if(print_verbose) fprintf(stderr, "INFO: Could not find last element of path.\n"); return 0; } static void usage(void) { fprintf(stderr, "Usage: reglookup [-v] [-s]" " [-p ] [-t ]" " \n"); fprintf(stderr, "Version: %s\n", regfi_version()); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t-v\t sets verbose mode.\n"); fprintf(stderr, "\t-h\t enables header row. (default)\n"); fprintf(stderr, "\t-H\t disables header row.\n"); fprintf(stderr, "\t-s\t enables security descriptor output.\n"); 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"); } int main(int argc, char** argv) { char** path = NULL; REGFI_ITERATOR* iter; int retr_path_ret, fd; uint32_t argi, arge; /* Process command line arguments */ if(argc < 2) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n"); } arge = argc-1; for(argi = 1; argi < arge; argi++) { if (strcmp("-p", argv[argi]) == 0) { if(++argi >= arge) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n"); } if((path_filter = strdup(argv[argi])) == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n"); path_filter_enabled = true; } else if (strcmp("-t", argv[argi]) == 0) { if(++argi >= arge) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n"); } if((type_filter = regfi_type_str2val(argv[argi])) < 0) { fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]); bailOut(REGLOOKUP_EXIT_USAGE, ""); } type_filter_enabled = true; } else if (strcmp("-h", argv[argi]) == 0) print_header = true; else if (strcmp("-H", argv[argi]) == 0) print_header = false; else if (strcmp("-s", argv[argi]) == 0) print_security = true; else if (strcmp("-S", argv[argi]) == 0) 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(); fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]); bailOut(REGLOOKUP_EXIT_USAGE, ""); } } registry_file = argv[argi]; if(print_verbose) regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR); fd = openHive(registry_file); if(fd < 0) { fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file); bailOut(REGLOOKUP_EXIT_NOINPUT, ""); } /* XXX: add command line option to choose output encoding */ f = regfi_alloc(fd, REGFI_ENCODING_ASCII); if(f == NULL) { close(fd); bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n"); } iter = regfi_iterator_new(f); if(iter == NULL) { printMsgs(f); bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n"); } if(print_header) { if(print_security) printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n"); else printf("PATH,TYPE,VALUE,MTIME\n"); } if(path_filter_enabled && path_filter != NULL) path = splitPath(path_filter); if(path != NULL) { retr_path_ret = retrievePath(iter, path); printMsgs(iter->f); freePath(path); if(retr_path_ret == 0) fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter); else if (retr_path_ret == 2) printKeyTree(iter); else if(retr_path_ret < 0) { fprintf(stderr, "ERROR: retrievePath() returned %d.\n", retr_path_ret); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unknown error occurred in retrieving path.\n"); } } else printKeyTree(iter); regfi_iterator_free(iter); regfi_free(f); close(fd); return 0; } reglookup+git/LICENSE000664 001750 001750 00000104374 12566244653 015521 0ustar00sophiesophie000000 000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . reglookup+git/bin/reglookup-timeline000775 001750 001750 00000002711 12566244653 021015 0ustar00sophiesophie000000 000000 #!/bin/sh # This script is a wrapper for reglookup, and reads one or more registry # files to produce an MTIME sorted output. This is helpful when building # timelines for investigations. # # 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 # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # 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$ usage() { echo "Usage: $0 [-H] [-V] [ ...]" 1>&2 echo " -H Omit header line" 1>&2 echo " -V Include values with parent timestamps" 1>&2 } if [ $# -eq 0 ]; then usage echo "ERROR: requires at least one parameter" 1>&2 exit 1 fi PRINT_HEADER=true if [ "$1" = "-H" ]; then PRINT_HEADER=false 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 $OPTS -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }' done | sort reglookup+git/lib/000775 001750 001750 00000000000 12566244653 015251 5ustar00sophiesophie000000 000000 reglookup+git/doc/reglookup.1.docbook000664 001750 001750 00000026672 12566244653 020775 0ustar00sophiesophie000000 000000 reglookup 1 File Conversion Utilities reglookup Windows NT+ registry reader/lookup tool SYNOPSIS reglookup [options] registry-file DESCRIPTION reglookup is designed to read windows registry elements and print them out to stdout in a CSV-like format. It has filtering options to narrow the focus of the output. This tool is designed to work with on Windows NT-based registries. OPTIONS reglookup accepts the following parameters: Specify a path prefix filter. Only keys/values under this registry path will be output. Specify a type filter. Only elements which match this registry data type will be printed. Acceptable values are: NONE, SZ, EXPAND_SZ, BINARY, DWORD, DWORD_BE, LINK, MULTI_SZ, RSRC_LIST, RSRC_DESC, RSRC_REQ_LIST, QWORD and KEY . Enables the printing of a column header row. (default) 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. Disables the printing of a column header row. Adds five additional columns to output containing information from key security descriptors and rarely used fields. The columns are: owner, group, sacl, dacl, class. (This feature's output has not been extensively tested.) Disables the printing of security descriptor information. (default) Verbose output. Required argument. Specifies the location of the registry file to read. The system registry files should be found under: %SystemRoot%/system32/config. OUTPUT reglookup generates comma-separated values (CSV) and writes them to stdout. The format is designed to simplify parsing algorithms of other tools by quoting CSV special characters using a common hexadecimal format. Specifically, special characters or non-ascii bytes are converted to "%XX" where XX is the hexadecimal value for the byte. The number of columns or fields in each line is fixed for a given run of the program, but may vary based on the command line options provided. See the header line for information on which fields are available and what they contain. Some fields in some lines may contain sub-fields which require additional delimiters. If these sub-delimiters occur in these sub-fields, they are also encoded in the same way as commas or other special characters are. Currently, the second, third, and fourth level delimiters are "|", ":", and " ", respectively. These are particularly important to take note of when security attributes are printed. Please note that these delimiters may occur in fields that are not sub-delimited, and should not be interpreted as special. Security attributes of registry keys have a complex structure which is outlined here. Each key will generally have an associated ACL (Access Control List), which is made up of ACEs (Access Control Entries). Each ACE is delimited by the secondary delimiter mentioned above, "|". The fields within an ACE are delimited by the third-level delimiter, ":", and consist of a SID, the ACE type (ALLOW, DENY, etc), a list of access rights, and a list of flags. The last two fields are delimited by the fourth-level delimiter " ". These final lists are simply human-readable interpretations of bits. The access rights abbreviations are listed below along with their Microsoft-assigned names: QRY_VAL KEY_QUERY_VALUE SET_VAL KEY_SET_VALUE CREATE_KEY KEY_CREATE_SUB_KEY ENUM_KEYS KEY_ENUMERATE_SUB_KEYS NOTIFY KEY_NOTIFY CREATE_LNK KEY_CREATE_LINK WOW64_64 KEY_WOW64_64KEY WOW64_32 KEY_WOW64_32KEY DELETE DELETE R_CONT READ_CONTROL W_DAC WRITE_DAC W_OWNER WRITE_OWNER SYNC SYNCHRONIZE SYS_SEC ACCESS_SYSTEM_SECURITY MAX_ALLWD MAXIMUM_ALLOWED GEN_A GENERIC_ALL GEN_X GENERIC_EXECUTE GEN_W GENERIC_WRITE GEN_R GENERIC_READ And the meaning of each flag is: OI Object Inherit CI Container Inherit NP Non-Propagate IO Inherit Only IA Inherited ACE Please see the following references for more information: http://msdn2.microsoft.com/en-gb/library/ms724878.aspx http://msdn2.microsoft.com/en-gb/library/aa374892.aspx http://msdn2.microsoft.com/en-us/library/aa772242.aspx http://support.microsoft.com/kb/220167 Note that some of the bits listed above have either not been allocated by Microsoft, or simply aren't documented. If any bits are set in the above two fields that aren't recognized, a hexidecimal representation of all of these mystery bits will be included in the output. For instance, if the lowest bit and third lowest bit were not recognized while being set, the number "0x5" would be included as an element in the list. While the ACL/ACE output format is mostly stable at this point, minor changes may be introduced in future versions. EXAMPLES To read and print the contents of an entire system registry file: reglookup /mnt/win/c/WINNT/system32/config/system To limit the output to just those entries under the Services key: reglookup -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system To limit the output to all registry values of type BINARY: reglookup -t BINARY /mnt/win/c/WINNT/system32/config/system And to limit the output to BINARY values under the Services key: reglookup -t BINARY -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system BUGS This program has been smoke-tested against most current Windows target platforms, but a comprehensive test suite has not yet been developed. (Please report results to the development mailing list if you encounter any bugs. Sample registry files and/or patches are greatly appreciated.) The SID conversions haven't been carefully checked for accuracy. The MTIME conversions appear correctly produce the stored UTC timestamp. However, due to the periodicity of registry writes, and the complexity of the conversion, a small amount of error (on the order of seconds) may be possible. The documentation available online from Microsoft on this field is very poor. For more information on registry format details, see: http://sentinelchicken.com/research/registry_format/ CREDITS This program was initially based on editreg.c by Richard Sharpe. It has since been rewritten to use a modified version the regfio library written by Gerald Carter. Heavy modifications to the library and the original command line interface have been done by Timothy D. Morgan. Please see source code for a full list of copyrights. LICENSE Please see the file "LICENSE" included with this software distribution. 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 version 3 for more details. SEE ALSO reglookup-timeline(1) reglookup-recover(1) reglookup+git/lib/winsec.c000664 001750 001750 00000035154 12566244653 016715 0ustar00sophiesophie000000 000000 /* * Copyright (C) 2005-2006,2009-2011,2015 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$ */ /** @file */ #include "winsec.h" /****************************************************************************** * Non-talloc() interface for parsing a descriptor. ******************************************************************************/ WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len) { return winsec_parse_desc(NULL, buf, buf_len); } /****************************************************************************** * Free a descriptor. Not needed if using talloc and a parent context is freed. ******************************************************************************/ void winsec_free_descriptor(WINSEC_DESC* desc) { talloc_free(desc); } /****************************************************************************** * Parses a WINSEC_DESC structure and substructures. ******************************************************************************/ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { WINSEC_DESC* ret_val; if (buf == NULL || buf_len < WINSEC_DESC_HEADER_SIZE) return NULL; if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL) return NULL; ret_val->revision = buf[0]; ret_val->sbz1 = buf[1]; ret_val->control = SVAL(buf, 0x2); /* XXX: should probably reject any non-self relative */ if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE)) fprintf(stderr, "DEBUG: NOT self-relative!\n"); ret_val->off_owner_sid = IVAL(buf, 0x4); ret_val->off_grp_sid = IVAL(buf, 0x8); ret_val->off_sacl = IVAL(buf, 0xC); ret_val->off_dacl = IVAL(buf, 0x10); /* A basic sanity check to ensure our offsets are within our buffer. * Additional length checking is done in secondary parsing functions. */ if((ret_val->off_owner_sid >= buf_len) || (ret_val->off_grp_sid >= buf_len) || (ret_val->off_sacl >= buf_len) || (ret_val->off_dacl >= buf_len)) { talloc_free(ret_val); return NULL; } if(ret_val->off_owner_sid == 0) ret_val->owner_sid = NULL; else { ret_val->owner_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_owner_sid, buf_len - ret_val->off_owner_sid); if(ret_val->owner_sid == NULL) { talloc_free(ret_val); return NULL; } } if(ret_val->off_grp_sid == 0) ret_val->grp_sid = NULL; else { ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid, buf_len - ret_val->off_grp_sid); if(ret_val->grp_sid == NULL) { talloc_free(ret_val); return NULL; } } if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl) { ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl, buf_len - ret_val->off_sacl); if(ret_val->sacl == NULL) { talloc_free(ret_val); return NULL; } } else ret_val->sacl = NULL; if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0) { ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl, buf_len - ret_val->off_dacl); if(ret_val->dacl == NULL) { talloc_free(ret_val); return NULL; } } else ret_val->dacl = NULL; return ret_val; } /****************************************************************************** * Parses a WINSEC_ACL structure and all substructures. ******************************************************************************/ WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { uint32_t i, offset; WINSEC_ACL* ret_val; /* * Note that the size is always a multiple of 4 bytes due to the * nature of the data structure. */ if (buf == NULL || buf_len < 8) return NULL; if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL) return NULL; ret_val->revision = SVAL(buf, 0x0); ret_val->size = SVAL(buf, 0x2); ret_val->num_aces = IVAL(buf, 0x4); /* The num_aces can be at most around 4k because anything greater * wouldn't fit in the 16 bit size even if every ace was as small as * possible. */ if((ret_val->size > buf_len) || (ret_val->num_aces > 4095)) { talloc_free(ret_val); return NULL; } /* Even if the num_aces is zero, allocate memory as there's a difference * between a non-present DACL (allow all access) and a DACL with no ACE's * (allow no access). */ if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*, ret_val->num_aces+1)) == NULL) { talloc_free(ret_val); return NULL; } offset = 8; for(i=0; i < ret_val->num_aces; i++) { ret_val->aces[i] = winsec_parse_ace(ret_val->aces, buf+offset, buf_len-offset); if(ret_val->aces[i] == NULL) { talloc_free(ret_val); return NULL; } offset += ret_val->aces[i]->size; if(offset > buf_len) { talloc_free(ret_val); return NULL; } } ret_val->aces[ret_val->num_aces] = NULL; return ret_val; } /****************************************************************************** * Parses a WINSEC_ACE structure and all substructures. ******************************************************************************/ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { uint32_t offset; WINSEC_ACE* ret_val; if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE) return NULL; if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL) return NULL; ret_val->type = buf[0]; ret_val->flags = buf[1]; ret_val->size = SVAL(buf, 0x2); ret_val->access_mask = IVAL(buf, 0x4); ret_val->obj_guid = NULL; ret_val->inh_guid = NULL; offset = 0x8; /* check whether object access is present */ if (winsec_ace_object(ret_val->type)) { ret_val->obj_flags = IVAL(buf, offset); offset += 4; if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT) { ret_val->obj_guid = winsec_parse_uuid(ret_val, buf+offset, buf_len-offset); if(ret_val->obj_guid == NULL) { talloc_free(ret_val); return NULL; } offset += sizeof(WINSEC_UUID); } if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT) { ret_val->inh_guid = winsec_parse_uuid(ret_val, buf+offset, buf_len-offset); if(ret_val->inh_guid == NULL) { talloc_free(ret_val); return NULL; } offset += sizeof(WINSEC_UUID); } } ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset); if(ret_val->trustee == NULL) { talloc_free(ret_val); return NULL; } return ret_val; } /****************************************************************************** * Parses a WINSEC_DOM_SID structure. ******************************************************************************/ WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { uint32_t i; WINSEC_DOM_SID* ret_val; if(buf == NULL || buf_len < 8) return NULL; /* if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/ if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL) return NULL; ret_val->sid_rev_num = buf[0]; ret_val->num_auths = buf[1]; memcpy(ret_val->id_auth, buf+2, 6); /* XXX: should really issue a warning here... */ if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS) ret_val->num_auths = WINSEC_MAX_SUBAUTHS; if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8) { talloc_free(ret_val); return NULL; } for(i=0; i < ret_val->num_auths; i++) ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t)); return ret_val; } /****************************************************************************** * Parses a WINSEC_UUID struct. ******************************************************************************/ WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { WINSEC_UUID* ret_val; if(buf == NULL || buf_len < sizeof(WINSEC_UUID)) return false; if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL) return NULL; ret_val->time_low = IVAL(buf, 0x0); ret_val->time_mid = SVAL(buf, 0x4); ret_val->time_hi_and_version = SVAL(buf, 0x6); memcpy(ret_val->clock_seq, buf+0x8, 2); memcpy(ret_val->node, buf+0xB, 6); return ret_val; } /****************************************************************************** * Calculates the size of a SID. ******************************************************************************/ size_t winsec_sid_size(const WINSEC_DOM_SID* sid) { if (sid == NULL) return 0; return sid->num_auths * sizeof(uint32_t) + 8; } /****************************************************************************** * Compare the auth portion of two SIDs. ******************************************************************************/ int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2) { int i; if (sid1 == sid2) return 0; if (!sid1) return -1; if (!sid2) return 1; if (sid1->sid_rev_num != sid2->sid_rev_num) return sid1->sid_rev_num - sid2->sid_rev_num; for (i = 0; i < 6; i++) if (sid1->id_auth[i] != sid2->id_auth[i]) return sid1->id_auth[i] - sid2->id_auth[i]; return 0; } /****************************************************************************** * Compare two SIDs. ******************************************************************************/ int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2) { int i; if (sid1 == sid2) return 0; if (!sid1) return -1; if (!sid2) return 1; /* Compare most likely different rids, first: i.e start at end */ if (sid1->num_auths != sid2->num_auths) return sid1->num_auths - sid2->num_auths; for (i = sid1->num_auths-1; i >= 0; --i) if (sid1->sub_auths[i] != sid2->sub_auths[i]) return sid1->sub_auths[i] - sid2->sub_auths[i]; return winsec_sid_compare_auth(sid1, sid2); } /****************************************************************************** * Compare two SIDs. ******************************************************************************/ bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2) { return winsec_sid_compare(sid1, sid2) == 0; } /****************************************************************************** ******************************************************************************/ char* winsec_sid2str(const WINSEC_DOM_SID* sid) { uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24; uint32_t left = size; uint8_t comps; char* ret_val; if(sid == NULL) return NULL; comps = sid->num_auths; ret_val = malloc(size); if(ret_val == NULL) return NULL; if(comps > WINSEC_MAX_SUBAUTHS) comps = WINSEC_MAX_SUBAUTHS; left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]); for (i = 0; i < comps; i++) left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]); return ret_val; } /****************************************************************************** * Compares two WINSEC_DESC structures. ******************************************************************************/ bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2) { /* Trivial cases */ if (!s1 && !s2) return true; if (!s1 || !s2) return false; /* Check top level stuff */ if (s1->revision != s2->revision) return false; if (s1->control != s2->control) return false; /* Check owner and group */ if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid)) return false; if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid)) return false; /* Check ACLs present in one but not the other */ if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) || (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) { return false; } /* Sigh - we have to do it the hard way by iterating over all the ACEs in the ACLs */ if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl)) return false; return true; } /****************************************************************************** * Compares two WINSEC_ACL structures. ******************************************************************************/ bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2) { unsigned int i, j; /* Trivial cases */ if (!s1 && !s2) return true; if (!s1 || !s2) return false; /* Check top level stuff */ if (s1->revision != s2->revision) return false; if (s1->num_aces != s2->num_aces) return false; /* The ACEs could be in any order so check each ACE in s1 against each ACE in s2. */ for (i = 0; i < s1->num_aces; i++) { bool found = false; for (j = 0; j < s2->num_aces; j++) { if (winsec_ace_equal(s1->aces[i], s2->aces[j])) { found = true; break; } } if (!found) return false; } return true; } /****************************************************************************** * Compares two WINSEC_ACE structures. ******************************************************************************/ bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2) { /* Trivial cases */ if (!s1 && !s2) return true; if (!s1 || !s2) return false; /* Check top level stuff */ if (s1->type != s2->type || s1->flags != s2->flags || s1->access_mask != s2->access_mask) { return false; } /* Check SID */ if (!winsec_sid_equal(s1->trustee, s2->trustee)) return false; return true; } /****************************************************************************** * Check if ACE has OBJECT type. ******************************************************************************/ bool winsec_ace_object(uint8_t type) { if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT || type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT || type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) { return true; } return false; } reglookup+git/include/000775 001750 001750 00000000000 12566244653 016126 5ustar00sophiesophie000000 000000 reglookup+git/doc/devel/000775 001750 001750 00000000000 12566244653 016347 5ustar00sophiesophie000000 000000 reglookup+git/doc/devel/references.txt000664 001750 001750 00000002707 12566244653 021237 0ustar00sophiesophie000000 000000 - 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 - Nigel Williams. Much of the same information as provided in 'winntreg.txt', but with some code: http://www.wednesday.demon.co.uk/dosreg.html - 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/ reglookup+git/python/experimental/class_parser.py000664 001750 001750 00000305557 12566244653 023573 0ustar00sophiesophie000000 000000 import sys, os, re, pdb, StringIO DEBUG = 0 def log(msg): if DEBUG>0: sys.stderr.write(msg+"\n") def escape_for_string(string): result = string result = result.encode("string-escape") result = result.replace('"',r'\"') return result class Module: def __init__(self, name): self.name = name self.constants = set() self.classes = {} self.headers = '#include \n' self.files = [] def __str__(self): result = "Module %s\n" % (self.name) l = self.classes.values() l.sort() for attr in l: if attr.is_active(): result += " %s\n" % attr l = list(self.constants) l.sort() result += 'Constants:\n' for attr, type in l: result += " %s\n" % attr return result init_string = '' def initialization(self): result = self.init_string + """ talloc_set_log_fn((void(*)(const char*))printf); //talloc_enable_leak_report(); """ for cls in self.classes.values(): if cls.is_active(): result += cls.initialise() return result def add_constant(self, constant, type="numeric"): """ This will be called to add #define constant macros """ self.constants.add((constant, type)) def add_class(self, cls, handler): self.classes[cls.class_name] = cls ## Make a wrapper in the type dispatcher so we can handle ## passing this class from/to python type_dispatcher[cls.class_name] = handler def private_functions(self): """ Emits hard coded private functions for doing various things """ return """ /* The following is a static array mapping CLASS() pointers to their python wrappers. This is used to allow the correct wrapper to be chosen depending on the object type found - regardless of the prototype. This is basically a safer way for us to cast the correct python type depending on context rather than assuming a type based on the .h definition. For example consider the function AFFObject Resolver.open(uri, mode) The .h file implies that an AFFObject object is returned, but this is not true as most of the time an object of a derived class will be returned. In C we cast the returned value to the correct type. In the python wrapper we just instantiate the correct python object wrapper at runtime depending on the actual returned type. We use this lookup table to do so. */ static int TOTAL_CLASSES=0; /* This is a global reference to this module so classes can call each other. */ static PyObject *g_module = NULL; static struct python_wrapper_map_t { Object class_ref; PyTypeObject *python_type; } python_wrappers[%s]; /** This is a generic wrapper type */ typedef struct { PyObject_HEAD void *base; void *ctx; } Gen_wrapper; /* Create the relevant wrapper from the item based on the lookup table. */ Gen_wrapper *new_class_wrapper(Object item) { int i; Gen_wrapper *result; Object cls; /* Hack to get the current null_context */ void* null_context; int* tmp = talloc(NULL, int); null_context = talloc_parent(tmp); talloc_free(tmp); // Return None for a NULL pointer if(!item) { Py_INCREF(Py_None); return (Gen_wrapper *)Py_None; } // Search for subclasses for(cls=(Object)item->__class__; cls != cls->__super__; cls=cls->__super__) { for(i=0; ictx = talloc_asprintf(NULL, "new_class_wrapper %%s@%%p", NAMEOF(item), (void*)item); result->base = (void *)item; /* If its owned by the null_context it means that the function does not want to own the memory - we therefore steal it so it gets freed with the python object. */ if(talloc_parent(result->base) == null_context) talloc_steal(result->ctx, result->base); return result; } } } PyErr_Format(PyExc_RuntimeError, "Unable to find a wrapper for object %%s", NAMEOF(item)); return NULL; } static PyObject *resolve_exception(char **error_buff) { enum _error_type *type = aff4_get_current_error(error_buff); switch(*type) { case EProgrammingError: return PyExc_SystemError; case EKeyError: return PyExc_KeyError; case ERuntimeError: return PyExc_RuntimeError; case EWarning: return PyExc_AssertionError; default: return PyExc_RuntimeError; }; } static int type_check(PyObject *obj, PyTypeObject *type) { PyTypeObject *tmp; // Recurse through the inheritance tree and check if the types are expected if(obj) for(tmp = obj->ob_type; tmp && tmp != &PyBaseObject_Type; tmp = tmp->tp_base) { if(tmp == type) return 1; } return 0; } static int check_error() { if(!CheckError(EZero)) { char *buffer; PyObject *exception = resolve_exception(&buffer); PyErr_Format(exception, "%%s", buffer); ClearError(); return 1; } return 0; } #define CHECK_ERROR if(check_error()) goto error; """ % (len(self.classes)+1) def initialise_class(self, class_name, out, done = None): if done and class_name in done: return done.add(class_name) cls = self.classes[class_name] """ Write out class initialisation code into the main init function. """ if cls.is_active(): base_class = self.classes.get(cls.base_class_name) if base_class and base_class.is_active(): ## We have a base class - ensure it gets written out ## first: self.initialise_class(cls.base_class_name, out, done) ## Now assign ourselves as derived from them out.write(" %s_Type.tp_base = &%s_Type;" % ( cls.class_name, cls.base_class_name)) out.write(""" %(name)s_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&%(name)s_Type) < 0) return; Py_INCREF((PyObject *)&%(name)s_Type); PyModule_AddObject(m, "%(name)s", (PyObject *)&%(name)s_Type); """ % {'name': cls.class_name}) def write(self, out): ## Prepare all classes for cls in self.classes.values(): cls.prepare() out.write(""" /********************************************************************** Autogenerated module %s This module was autogenerated from the following files: """ % self.name) for file in self.files: out.write("%s\n" % file) out.write("\nThis module implements the following classes:\n") out.write(self.__str__()) out.write("""***********************************************************************/ """) out.write(self.headers) out.write(self.private_functions()) for cls in self.classes.values(): if cls.is_active(): out.write("/******************** %s ***********************/" % cls.class_name) cls.struct(out) cls.prototypes(out) out.write("/*****************************************************\n Implementation\n******************************************************/\n\n") for cls in self.classes.values(): if cls.is_active(): cls.PyMethodDef(out) cls.code(out) cls.PyTypeObject(out) ## Write the module initializer out.write(""" static PyMethodDef %(module)s_methods[] = { {NULL} /* Sentinel */ }; PyMODINIT_FUNC init%(module)s(void) { /* Make sure threads are enabled */ PyEval_InitThreads(); /* create module */ PyObject *m = Py_InitModule3("%(module)s", %(module)s_methods, "%(module)s module."); PyObject *d = PyModule_GetDict(m); PyObject *tmp; g_module = m; """ % {'module': self.name}) ## The trick is to initialise the classes in order of their ## inheritance. The following code will order initializations ## according to their inheritance tree done = set() for class_name in self.classes.keys(): self.initialise_class(class_name, out, done) ## Add the constants in here for constant, type in self.constants: if type == 'integer': out.write(""" tmp = PyLong_FromUnsignedLongLong((int64_t)%s); \n""" % constant) elif type == 'string': out.write(" tmp = PyString_FromString((char *)%s); \n" % constant) else: out.write(" // I dont know how to convert %s type %s\n" % (constant, type)) continue out.write(""" PyDict_SetItemString(d, "%s", tmp); Py_DECREF(tmp);\n""" % (constant)) out.write(self.initialization()) out.write("}\n\n") class Type: interface = None buildstr = 'O' sense = 'IN' error_value = "return 0;" active = True def __init__(self, name, type): self.name = name self.type = type self.attributes = set() def comment(self): return "%s %s " % (self.type, self.name) def python_name(self): return self.name def python_proxy_post_call(self): """ This is called after a proxy call """ return '' def returned_python_definition(self, *arg, **kw): return self.definition(*arg, **kw) def definition(self, default=None, **kw): if default: return "%s %s=%s;\n" % (self.type, self.name, default) else: return "%s __attribute__((unused)) %s;\n" % (self.type, self.name) def byref(self): return "&%s" % self.name def call_arg(self): return self.name def pre_call(self, method): return '' def assign(self, call, method, target=None): return "Py_BEGIN_ALLOW_THREADS\n%s = %s;\nPy_END_ALLOW_THREADS\n" % (target or self.name, call) def post_call(self, method): if "DESTRUCTOR" in self.attributes: return "talloc_free(self->ctx); self->base = NULL;\n" return '' def from_python_object(self, source, destination, method, **kw): return '' def return_value(self, value): return "return %s;" % value def __str__(self): if self.name == 'func_return': return self.type if 'void' in self.type: return '' result = "%s : %s" % (self.type, self.name) return result class String(Type): interface = 'string' buildstr = 's' error_value = "return NULL;" def __init__(self, name, type): Type.__init__(self, name, type) self.length = "strlen(%s)" % name def byref(self): return "&%s" % self.name def to_python_object(self, name=None, result='py_result',**kw): name = name or self.name result = """PyErr_Clear(); if(!%(name)s) { PyErr_Format(PyExc_RuntimeError, "%(name)s is NULL"); goto error; } %(result)s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\nif(!%(result)s) goto error; """ % dict(name=name, result=result,length=self.length) if "BORROWED" not in self.attributes and 'BORROWED' not in kw: result += "talloc_unlink(NULL, %s);\n" % name return result def from_python_object(self, source, destination, method, context='NULL'): method.error_set = True return """ { char *buff; Py_ssize_t length; PyErr_Clear(); if(-1==PyString_AsStringAndSize(%(source)s, &buff, &length)) goto error; %(destination)s = talloc_size(%(context)s, length + 1); memcpy(%(destination)s, buff, length); %(destination)s[length]=0; } """ % dict(source = source, destination = destination, context =context) class ZString(String): interface = 'null_terminated_string' class BorrowedString(String): def to_python_object(self, name=None, result='py_result', **kw): name = name or self.name return "PyErr_Clear();\n" +\ "%s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\n" % dict( name=name, length=self.length, result=result) class Char_and_Length(Type): interface = 'char_and_length' buildstr = 's#' error_value = "return NULL;" def __init__(self, data, data_type, length, length_type): Type.__init__(self, data, data_type) self.name = data self.data_type=data_type self.length = length self.length_type = length_type def comment(self): return "%s %s, %s %s" % (self.data_type, self.name, self.length_type, self.length) def definition(self, default = '""', **kw): return "char *%s=%s; Py_ssize_t %s=strlen(%s);\n" % ( self.name, default, self.length, default) def byref(self): return "&%s, &%s" % (self.name, self.length) def call_arg(self): return "(%s)%s, (%s)%s" % (self.data_type, self.name, self.length_type, self.length) def to_python_object(self, name=None, result='py_result', **kw): return "PyErr_Clear();\n"\ "%s = PyString_FromStringAndSize(%s, %s);\nif(!%s) goto error;" % ( result, self.name, self.length, result); class Integer(Type): interface = 'integer' buildstr = 'K' def __init__(self, name,type): Type.__init__(self,name,type) self.type = 'uint64_t' self.original_type = type def definition(self, default = 0, **kw): return Type.definition(self, default) def to_python_object(self, name=None, result='py_result', **kw): name = name or self.name return "PyErr_Clear();\n%s = PyLong_FromLongLong(%s);\n" % (result, name) def from_python_object(self, source, destination, method, **kw): return "PyErr_Clear();\n"\ "%(destination)s = PyLong_AsUnsignedLong(%(source)s);\n" % dict( source = source, destination= destination) def comment(self): return "%s %s " % (self.original_type, self.name) class Integer32(Integer): buildstr = 'I' def __init__(self, name,type): Type.__init__(self,name,type) self.type = 'unsigned int ' self.original_type = type def to_python_object(self, name=None, result='py_result', **kw): name = name or self.name return "PyErr_Clear();\n%s = PyLong_FromLong(%s);\n" % (result, name) class Integer64(Integer): buildstr = 'K' type = 'unsigned int' class Char(Integer): buildstr = "s" interface = 'small_integer' def to_python_object(self, name = None, result = 'py_result', **kw): ## We really want to return a string here return """{ char *str_%(name)s = &%(name)s; PyErr_Clear(); %(result)s = PyString_FromStringAndSize(str_%(name)s, 1); if(!%(result)s) goto error; } """ % dict(result=result, name = name or self.name) def definition(self, default = '"\\x0"', **kw): ## Shut up unused warnings return "char %s __attribute__((unused))=0;\nchar *str_%s __attribute__((unused)) = %s;\n" % ( self.name,self.name, default) def byref(self): return "&str_%s" % self.name def pre_call(self, method): method.error_set = True return """ if(strlen(str_%(name)s)!=1) { PyErr_Format(PyExc_RuntimeError, "You must only provide a single character for arg %(name)r"); goto error; } %(name)s = str_%(name)s[0]; """ % dict(name = self.name) class StringOut(String): sense = 'OUT' class IntegerOut(Integer): sense = 'OUT_DONE' buildstr = '' def python_name(self): return None def byref(self): return '' def call_arg(self): return "&%s" % self.name class Integer32Out(Integer32): sense = 'OUT_DONE' buildstr = '' def python_name(self): return None def byref(self): return '' def call_arg(self): return "&%s" % self.name class Char_and_Length_OUT(Char_and_Length): sense = 'OUT_DONE' buildstr = 'l' def definition(self, default = 0, **kw): return "char *%s=NULL; Py_ssize_t %s=%s;\n" % ( self.name, self.length, default) + "PyObject *tmp_%s;\n" % self.name def python_name(self): return self.length def byref(self): return "&%s" % self.length def pre_call(self, method): return """PyErr_Clear(); tmp_%s = PyString_FromStringAndSize(NULL, %s); if(!tmp_%s) goto error; PyString_AsStringAndSize(tmp_%s, &%s, (Py_ssize_t *)&%s); """ % (self.name, self.length, self.name, self.name, self.name, self.length) def to_python_object(self, name=None, result='py_result', sense='in', **kw): name = name or self.name if 'results' in kw: kw['results'].pop(0) if sense=='proxied': return "py_%s = PyLong_FromLong(%s);\n" % (self.name, self.length) return """if(func_return > %(length)s) { func_return = 0; } _PyString_Resize(&tmp_%(name)s, func_return); \n%(result)s = tmp_%(name)s;\n""" % \ dict(name= name, result= result, length=self.length) def python_proxy_post_call(self, result='py_result'): return """ { char *tmp_buff; int tmp_len; if(-1==PyString_AsStringAndSize(%(result)s, &tmp_buff, &tmp_len)) goto error; memcpy(%(name)s,tmp_buff, tmp_len); Py_DECREF(%(result)s); %(result)s = PyLong_FromLong(tmp_len); } """ % dict(result = result, name=self.name) class TDB_DATA_P(Char_and_Length_OUT): bare_type = "TDB_DATA" def __init__(self, name, type): Type.__init__(self, name, type) def definition(self, default=None, **kw): return Type.definition(self) def byref(self): return "%s.dptr, &%s.dsize" % (self.name, self.name) def pre_call(self, method): return '' def call_arg(self): return Type.call_arg(self) def to_python_object(self, name=None,result='py_result', **kw): name = name or self.name return "PyErr_Clear();"\ "%s = PyString_FromStringAndSize((char *)%s->dptr, %s->dsize);"\ "\ntalloc_free(%s);" % (result, name, name, name) def from_python_object(self, source, destination, method, **kw): method.error_set = True return """ %(destination)s = talloc(self, %(bare_type)s); { Py_ssize_t tmp; char *buf; PyErr_Clear(); if(-1==PyString_AsStringAndSize(%(source)s, &buf, &tmp)) { goto error; } // Take a copy of the python string %(destination)s->dptr = talloc_memdup(%(destination)s, buf, tmp); %(destination)s->dsize = tmp; } // We no longer need the python object Py_DECREF(%(source)s); """ % dict(source = source, destination = destination, bare_type = self.bare_type) class TDB_DATA(TDB_DATA_P): def to_python_object(self, name = None, result='py_result', **kw): name = name or self.name return "PyErr_Clear();\n"\ "%s = PyString_FromStringAndSize((char *)%s.dptr, %s.dsize);\n" % ( result, name, name) class Void(Type): buildstr = '' error_value = "return;" original_type = '' def __init__(self, *args): Type.__init__(self, None, 'void *') def definition(self, default = None, **kw): return '' def to_python_object(self, name=None, result = 'py_result', **kw): return "Py_INCREF(Py_None); py_result = Py_None;\n" def call_arg(self): return "NULL" def byref(self): return None def assign(self, call, method, target=None): ## We dont assign the result to anything return "Py_BEGIN_ALLOW_THREADS\n%s;\nPy_END_ALLOW_THREADS\n" % call def return_value(self, value): return "return;" class StringArray(String): interface = 'array' buildstr = 'O' def definition(self, default = '""', **kw): return "char **%s=NULL; PyObject *py_%s=NULL;\n" % ( self.name, self.name) def byref(self): return "&py_%s" % (self.name) def from_python_object(self, source, destination, method, context='NULL'): method.error_set = True return """{ Py_ssize_t i,size=0; if(%(source)s) { if(!PySequence_Check(%(source)s)) { PyErr_Format(PyExc_ValueError, "%(destination)s must be a sequence"); goto error; } size = PySequence_Size(%(source)s); } %(destination)s = talloc_zero_array(NULL, char *, size + 1); for(i=0; ibase; """ % dict(source = source, destination = destination, type = self.type) def to_python_object(self, **kw): return '' def returned_python_definition(self, default = 'NULL', sense='in', **kw): return "%s %s;\n" % (self.type, self.name) def definition(self, default = 'NULL', sense='in', **kw): result = "Gen_wrapper *%s __attribute__((unused)) = %s;\n" % (self.name, default) if sense == 'in' and not 'OUT' in self.attributes: result += " %s __attribute__((unused)) call_%s;\n" % (self.type, self.name) return result def call_arg(self): return "call_%s" % self.name def pre_call(self, method): if 'OUT' in self.attributes or self.sense == 'OUT': return '' return """ if(!%(name)s || (PyObject *)%(name)s==Py_None) { call_%(name)s = NULL; } else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) { PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s"); goto error; } else { call_%(name)s = %(name)s->base; }\n""" % self.__dict__ def assign(self, call, method, target=None): method.error_set = True; args = dict(name = target or self.name, call = call, type = self.type) result = """{ Object returned_object; ClearError(); Py_BEGIN_ALLOW_THREADS returned_object = (Object)%(call)s; Py_END_ALLOW_THREADS CHECK_ERROR; """ % args ## Is NULL an acceptable return type? In some python code NULL ## can be returned (e.g. in iterators) but usually it should ## be converted to None. if "NULL_OK" in self.attributes: result += """if(returned_object == NULL) { %(name)s = NULL; """ % args else: result += """ // A NULL return without errors means we return None if(!returned_object) { %(name)s = (Gen_wrapper *)Py_None; Py_INCREF(Py_None); """ % args result += """ } else { //printf("%%s: Wrapping %%s@%%p\\n", __FUNCTION__, NAMEOF(returned_object), returned_object); %(name)s = new_class_wrapper(returned_object); if(!%(name)s) goto error; """ % args if "BORROWED" in self.attributes: result += " talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args result += """ } } """ return result def to_python_object(self, name=None, result = 'py_result', sense='in', **kw): name = name or self.name args = dict(result=result, name = name) if sense=='proxied': return "%(result)s = (PyObject *)new_class_wrapper((Object)%(name)s);\n" % args return "%(result)s = (PyObject *)%(name)s;\n" % args class PointerWrapper(Wrapper): """ A pointer to a wrapped class """ def __init__(self, name, type): type = type.split()[0] Wrapper.__init__(self,name, type) def definition(self, default = 'NULL', sense='in', **kw): result = "Gen_wrapper *%s = %s;" % (self.name, default) if sense == 'in' and not 'OUT' in self.attributes: result += " %s *call_%s;\n" % (self.type, self.name) return result def pre_call(self, method): if 'OUT' in self.attributes or self.sense == 'OUT': return '' return """ if(!%(name)s || (PyObject *)%(name)s==Py_None) { call_%(name)s = NULL; } else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) { PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s"); goto error; } else { call_%(name)s = (%(type)s *)&%(name)s->base; }\n""" % self.__dict__ class StructWrapper(Wrapper): """ A wrapper for struct classes """ def assign(self, call, method, target = None): args = dict(name = target or self.name, call = call, type = self.type) result = """ PyErr_Clear(); %(name)s = (Gen_wrapper *)PyObject_New(py%(type)s, &%(type)s_Type); %(name)s->ctx = talloc_size(NULL, 1); %(name)s->base = %(call)s; """ % args if "NULL_OK" in self.attributes: result += "if(!%(name)s->base) { Py_DECREF(%(name)s); return NULL; }" % args result += """ // A NULL object gets translated to a None if(!%(name)s->base) { Py_DECREF(%(name)s); Py_INCREF(Py_None); %(name)s = (Gen_wrapper *)Py_None; } else { """ % args if "FOREIGN" in self.attributes: result += '// Not taking references to foreign memory\n' elif "BORROWED" in self.attributes: result += "talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args else: result += "talloc_steal(%(name)s->ctx, %(name)s->base);\n" % args result += "}\n" return result def byref(self): return "&%s" % self.name def definition(self, default = 'NULL', sense='in', **kw): result = "Gen_wrapper *%s = %s;" % (self.name, default) if sense == 'in' and not 'OUT' in self.attributes: result += " %s *call_%s;\n" % (self.type, self.name) return result; class PointerStructWrapper(StructWrapper): def __init__(self, name, type): type = type.split()[0] Wrapper.__init__(self,name, type) class Timeval(Type): """ handle struct timeval values """ interface = 'numeric' buildstr = 'f' def definition(self, default = None, **kw): return "float %(name)s_flt; struct timeval %(name)s;\n" % self.__dict__ def byref(self): return "&%s_flt" % self.name def pre_call(self, method): return "%(name)s.tv_sec = (int)%(name)s_flt; %(name)s.tv_usec = (%(name)s_flt - %(name)s.tv_sec) * 1e6;\n" % self.__dict__ def to_python_object(self, name=None, result = 'py_result', **kw): name = name or self.name return """%(name)s_flt = (double)(%(name)s.tv_sec) + %(name)s.tv_usec; %(result)s = PyFloat_FromDouble(%(name)s_flt); """ % dict(name = name, result=result) class PyObject(Type): """ Accept an opaque python object """ interface = 'opaque' buildstr = 'O' def definition(self, default = 'NULL', **kw): self.default = default return 'PyObject *%(name)s = %(default)s;\n' % self.__dict__ def byref(self): return "&%s" % self.name type_dispatcher = { "IN char *": String, "IN unsigned char *": String, "unsigned char *": String, "char *": String, "ZString": ZString, "OUT char *": StringOut, "OUT unsigned char *": StringOut, "unsigned int": Integer, 'int': Integer, 'OUT uint64_t *': IntegerOut, 'OUT uint32_t *': Integer32Out, 'char': Char, 'void': Void, 'void *': Void, 'TDB_DATA *': TDB_DATA_P, 'TDB_DATA': TDB_DATA, 'uint64_t': Integer, 'uint32_t': Integer32, 'time_t': Integer32, 'uint16_t': Integer, 'uint8_t': Integer, 'int64_t': Integer, 'ssize_t': Integer, 'size_t': Integer, 'unsigned long int': Integer, 'struct timeval': Timeval, 'char **': StringArray, 'PyObject *': PyObject, } method_attributes = ['BORROWED', 'DESTRUCTOR','IGNORE'] def dispatch(name, type): if not type: return Void() m = re.match("struct ([a-zA-Z0-9]+)_t *", type) if m: type = m.group(1) type_components = type.split() attributes = set() if type_components[0] in method_attributes: attributes.add(type_components.pop(0)) type = " ".join(type_components) result = type_dispatcher[type](name, type) result.attributes = attributes return result class ResultException: value = 0 exception = "PyExc_IOError" def __init__(self, check, exception, message): self.check = check self.exception = exception self.message = message def write(self, out): out.write("\n//Handle exceptions\n") out.write("if(%s) {\n PyErr_Format(PyExc_%s, %s);\n goto error; \n}\n\n" % ( self.check, self.exception, self.message)) class Method: default_re = re.compile("DEFAULT\(([A-Z_a-z0-9]+)\) =(.+)") exception_re = re.compile("RAISES\(([^,]+),\s*([^\)]+)\) =(.+)") typedefed_re = re.compile(r"struct (.+)_t \*") def __init__(self, class_name, base_class_name, method_name, args, return_type, myclass = None): self.name = method_name ## myclass needs to be a class generator if not isinstance(myclass, ClassGenerator): raise RuntimeError("myclass must be a class generator") self.myclass = myclass self.docstring = '' self.defaults = {} self.exception = None self.error_set = False self.class_name = class_name self.base_class_name = base_class_name self.args = [] self.definition_class_name = class_name for type,name in args: self.add_arg(type, name) try: self.return_type = dispatch('func_return', return_type) self.return_type.attributes.add("OUT") self.return_type.original_type = return_type except KeyError: ## Is it a wrapped type? if return_type: log("Unable to handle return type %s.%s %s" % (self.class_name, self.name, return_type)) #pdb.set_trace() self.return_type = Void() def clone(self, new_class_name): self.find_optional_vars() result = self.__class__(new_class_name, self.base_class_name, self.name, [], 'void *', myclass = self.myclass) result.args = self.args result.return_type = self.return_type result.definition_class_name = self.definition_class_name result.defaults = self.defaults result.exception = self.exception return result def find_optional_vars(self): for line in self.docstring.splitlines(): m =self.default_re.search(line) if m: name = m.group(1) value = m.group(2) log("Setting default value for %s of %s" % (m.group(1), m.group(2))) self.defaults[name] = value m =self.exception_re.search(line) if m: self.exception = ResultException(m.group(1), m.group(2), m.group(3)) def write_local_vars(self, out): self.find_optional_vars() ## We do it in two passes - first mandatory then optional kwlist = """static char *kwlist[] = {""" ## Mandatory for type in self.args: python_name = type.python_name() if python_name and python_name not in self.defaults: kwlist += '"%s",' % python_name for type in self.args: python_name = type.python_name() if python_name and python_name in self.defaults: kwlist += '"%s",' % python_name kwlist += ' NULL};\n' for type in self.args: python_name = type.python_name() try: out.write(type.definition(default = self.defaults[python_name])) except KeyError: out.write(type.definition()) ## Make up the format string for the parse args in two pases parse_line = '' for type in self.args: python_name = type.python_name() if type.buildstr and python_name not in self.defaults: parse_line += type.buildstr parse_line += '|' for type in self.args: python_name = type.python_name() if type.buildstr and python_name in self.defaults: parse_line += type.buildstr if parse_line != '|': ## Now parse the args from python objects out.write(kwlist) out.write("\nif(!PyArg_ParseTupleAndKeywords(args, kwds, \"%s\", kwlist, " % parse_line) tmp = [] for type in self.args: ref = type.byref() if ref: tmp.append(ref) out.write(",".join(tmp)) self.error_set = True out.write("))\n goto error;\n\n") def error_condition(self): result = "" if "DESTRUCTOR" in self.return_type.attributes: result += "talloc_free(self->ctx); self->base = NULL;\n" return result +"return NULL;\n"; def write_definition(self, out): args = dict(method = self.name, class_name = self.class_name) out.write("\n/********************************************************\nAutogenerated wrapper for function:\n") out.write(self.comment()) out.write("********************************************************/\n") self._prototype(out) out.write("""{ PyObject *returned_result, *py_result; """ % args) out.write(self.return_type.definition()) self.write_local_vars( out); out.write("""// Make sure that we have something valid to wrap if(!self->base) return PyErr_Format(PyExc_RuntimeError, "%(class_name)s object no longer valid"); """ % args) ## Precall preparations out.write("// Precall preparations\n") out.write(self.return_type.pre_call(self)) for type in self.args: out.write(type.pre_call(self)) out.write("""// Check the function is implemented { void *method = ((%(def_class_name)s)self->base)->%(method)s; if(!method || (void *)unimplemented == (void *)method) { PyErr_Format(PyExc_RuntimeError, "%(class_name)s.%(method)s is not implemented"); goto error; } } """ % dict(def_class_name = self.definition_class_name, method=self.name, class_name = self.class_name)) out.write("\n// Make the call\n ClearError();") call = "((%s)self->base)->%s(((%s)self->base)" % (self.definition_class_name, self.name, self.definition_class_name) tmp = '' for type in self.args: tmp += ", " + type.call_arg() call += "%s)" % tmp ## Now call the wrapped function out.write(self.return_type.assign(call, self)) if self.exception: self.exception.write(out) self.error_set = True out.write("\n// Postcall preparations\n") ## Postcall preparations out.write(self.return_type.post_call(self)) for type in self.args: out.write(type.post_call(self)) ## Now assemble the results results = [self.return_type.to_python_object()] for type in self.args: if type.sense == 'OUT_DONE': results.append(type.to_python_object(results = results)) ## If all the results are returned by reference we dont need ## to prepend the void return value at all. if isinstance(self.return_type, Void) and len(results)>1: results.pop(0) out.write("\n// prepare results\n") ## Make a tuple of results and pass them back if len(results)>1: out.write("returned_result = PyList_New(0);\n") for result in results: out.write(result) out.write("PyList_Append(returned_result, py_result); Py_DECREF(py_result);\n"); out.write("return returned_result;\n") else: out.write(results[0]) ## This useless code removes compiler warnings out.write("returned_result = py_result;\nreturn returned_result;\n"); ## Write the error part of the function if self.error_set: out.write("\n// error conditions:\n") out.write("error:\n " + self.error_condition()); out.write("\n}\n\n") def add_arg(self, type, name): try: t = type_dispatcher[type](name, type) except KeyError: ## Sometimes types must be typedefed in advance try: m = self.typedefed_re.match(type) type = m.group(1) log( "Trying %s for %s" % (type, m.group(0))) t = type_dispatcher[type](name, type) except (KeyError, AttributeError): log( "Unable to handle type %s.%s %s" % (self.class_name, self.name, type)) return ## Here we collapse char * + int type interfaces into a ## coherent string like interface. try: previous = self.args[-1] if t.interface == 'integer' and \ previous.interface == 'string': ## We make a distinction between IN variables and OUT ## variables if previous.sense == 'OUT': cls = Char_and_Length_OUT else: cls = Char_and_Length cls = cls( previous.name, previous.type, name, type) self.args[-1] = cls return except IndexError: pass self.args.append(t) def comment(self): result = self.return_type.original_type+" "+self.class_name+"."+self.name+"(" args = [] for type in self.args: args.append( type.comment()) result += ",".join(args) + ");\n" return result def prototype(self, out): self._prototype(out) out.write(";\n") def _prototype(self, out): out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self, PyObject *args, PyObject *kwds) """ % dict(method = self.name, class_name = self.class_name)) def __str__(self): result = "def %s %s(%s):" % ( self.return_type, self.name, ' , '.join([a.__str__() for a in self.args])) return result def PyMethodDef(self, out): docstring = self.comment() + "\n\n" + self.docstring out.write(' {"%s",(PyCFunction)py%s_%s, METH_VARARGS|METH_KEYWORDS, "%s"},\n' % ( self.name, self.class_name, self.name, escape_for_string(docstring))) class IteratorMethod(Method): """ A Method which implements an iterator """ def __init__(self, *args, **kw): Method.__init__(self, *args, **kw) ## Tell the return type that a NULL python return is ok self.return_type.attributes.add("NULL_OK") def _prototype(self, out): out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self)""" % dict(method = self.name, class_name = self.class_name)) def __str__(self): result = "Iterator returning %s." % ( self.return_type) return result def PyMethodDef(self, out): ## This method should not go in the method table as its linked ## in directly pass class SelfIteratorMethod(IteratorMethod): def write_definition(self, out): args = dict(method = self.name, class_name = self.class_name) out.write("\n/********************************************************\nAutogenerated wrapper for function:\n") out.write(self.comment()) out.write("********************************************************/\n") self._prototype(out) out.write("""{ ((%(class_name)s)self->base)->%(method)s((%(class_name)s)self->base); return PyObject_SelfIter((PyObject *)self); } """ % args) class ConstructorMethod(Method): ## Python constructors are a bit different than regular methods def _prototype(self, out): out.write(""" static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) """ % dict(method = self.name, class_name = self.class_name)) def write_destructor(self, out): ## We make sure that we unlink exactly the reference we need ## (the object will persist if it has some other ## references). Note that if we just used talloc_free here it ## will remove some random reference which may not actually be ## the reference we own (which is NULL). free = """ if(self->base) { //printf("Unlinking %s@%p\\n", NAMEOF(self->base), self->base); talloc_free(self->ctx); self->base=NULL; } """ out.write("""static void %(class_name)s_dealloc(py%(class_name)s *self) { %(free)s PyObject_Del(self); }\n """ % dict(class_name = self.class_name, free=free)) def error_condition(self): return "return -1;"; def write_definition(self, out): self._prototype(out) out.write("""{\n""") self.write_local_vars(out) ## Precall preparations for type in self.args: out.write(type.pre_call(self)) ## Now call the wrapped function out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name) out.write("\n ClearError();\nPy_BEGIN_ALLOW_THREADS\nself->base = CONSTRUCT(%s, %s, %s, self->ctx" % ( self.class_name, self.definition_class_name, self.name)) tmp = '' for type in self.args: tmp += ", " + type.call_arg() self.error_set = True out.write("""%s);\nPy_END_ALLOW_THREADS\n if(!CheckError(EZero)) { char *buffer; PyObject *exception = resolve_exception(&buffer); PyErr_Format(exception, "%%s", buffer); ClearError(); goto error; } else if(!self->base) { PyErr_Format(PyExc_IOError, "Unable to construct class %s"); goto error; } """ % (tmp, self.class_name)) out.write(" return 0;\n"); ## Write the error part of the function if self.error_set: out.write("error:\n " + self.error_condition()); out.write("\n}\n\n") class GetattrMethod(Method): def __init__(self, class_name, base_class_name, myclass): self.base_class_name = base_class_name self._attributes = [] self.error_set = True self.return_type = Void() self.myclass = myclass self.rename_class_name(class_name) def add_attribute(self, attr): if attr.name: self._attributes.append([self.class_name, attr]) def rename_class_name(self, new_name): """ This allows us to rename the class_name at a later stage. Required for late initialization of Structs whose name is not know until much later on. """ self.class_name = new_name self.name = "py%s_getattr" % new_name for x in self._attributes: x[0] = new_name def get_attributes(self): for class_name, attr in self._attributes: try: if not self.myclass.module.classes[attr.type].active: continue except KeyError: pass yield class_name, attr def __str__(self): result = "" for class_name, attr in self.get_attributes(): result += " %s\n" % attr.__str__() return result def clone(self, class_name): result = self.__class__(class_name, self.base_class_name, self.myclass) result._attributes = self._attributes[:] return result def prototype(self, out): if self.name: out.write(""" static PyObject *%(name)s(py%(class_name)s *self, PyObject *name); """ % self.__dict__) def built_ins(self, out): """ check for some built in attributes we need to support """ out.write(""" if(!strcmp(name, "__members__")) { PyObject *result = PyList_New(0); PyObject *tmp; PyMethodDef *i; if(!result) goto error; """) ## Add attributes for class_name, attr in self.get_attributes(): out.write(""" tmp = PyString_FromString("%(name)s"); PyList_Append(result, tmp); Py_DECREF(tmp); """ % dict(name = attr.name)) ## Add methods out.write(""" for(i=%s_methods; i->ml_name; i++) { tmp = PyString_FromString(i->ml_name); PyList_Append(result, tmp); Py_DECREF(tmp); }""" % self.class_name) out.write(""" return result; }\n""") def write_definition(self, out): if not self.name: return out.write(""" static PyObject *py%(class_name)s_getattr(py%(class_name)s *self, PyObject *pyname) { char *name; // Try to hand it off to the python native handler first PyObject *result = PyObject_GenericGetAttr((PyObject*)self, pyname); if(result) return result; PyErr_Clear(); // No - nothing interesting was found by python name = PyString_AsString(pyname); if(!self->base) return PyErr_Format(PyExc_RuntimeError, "Wrapped object (%(class_name)s.%(name)s) no longer valid"); if(!name) return NULL; """ % self.__dict__) self.built_ins(out) for class_name, attr in self.get_attributes(): ## what we want to assign if self.base_class_name: call = "(((%s)self->base)->%s)" % (class_name, attr.name) else: call = "(self->base->%s)" % (attr.name) out.write(""" if(!strcmp(name, "%(name)s")) { PyObject *py_result; %(python_def)s %(python_assign)s %(python_obj)s return py_result; }""" % dict(name = attr.name, python_obj = attr.to_python_object(), python_assign = attr.assign(call, self), python_def = attr.definition(sense='out'))) out.write(""" return PyObject_GenericGetAttr((PyObject *)self, pyname); """ % self.__dict__) ## Write the error part of the function if self.error_set: out.write("error:\n" + self.error_condition()); out.write("}\n\n") class ProxiedGetattr(GetattrMethod): def built_ins(self,out): out.write(""" if(!strcmp(name, "__members__")) { PyObject *result; PyObject *tmp; PyMethodDef *i; PyErr_Clear(); // Get the list of members from our proxied object result = PyObject_GetAttrString(self->base->proxied, name); if(!result) goto error; """) ## Add attributes for class_name, attr in self.get_attributes(): out.write(""" tmp = PyString_FromString("%(name)s"); PyList_Append(result, tmp); Py_DECREF(tmp); """ % dict(name = attr.name)) ## Add methods out.write(""" for(i=%s_methods; i->ml_name; i++) { tmp = PyString_FromString(i->ml_name); PyList_Append(result, tmp); Py_DECREF(tmp); } """ % self.class_name) out.write(""" return result; }\n""") out.write(""" /** Just try to get the attribute from our proxied object */ { PyObject *result = PyObject_GetAttrString(self->base->proxied, name); if(result) return result; } """) class ProxiedMethod(Method): def __init__(self, method, myclass): self.name = method.name self.method = method self.myclass = myclass self.class_name = method.class_name self.base_class_name = method.base_class_name self.args = method.args self.definition_class_name = method.definition_class_name self.return_type = method.return_type self.docstring = "Proxy for %s" % self.name self.defaults = {} self.exception = None self.error_set = False def get_name(self): return "py%(class_name)s_%(name)s" % dict(class_name =self.myclass.class_name, name = self.name) def _prototype(self, out): out.write(""" static %(return_type)s %(name)s(%(definition_class_name)s self""" % dict( return_type = self.return_type.original_type, class_name = self.myclass.class_name, method = self.name, name = self.get_name(), definition_class_name = self.definition_class_name)) for arg in self.args: out.write(", %s" % (arg.comment())) out.write(")") def prototype(self, out): self._prototype(out) out.write(";\n") def write_definition(self, out): self._prototype(out) self._write_definition(out) def _write_definition(self, out): ## We need to grab the GIL before we do anything out.write("""{ //Grab the GIL so we can do python stuff PyGILState_STATE gstate; gstate = PyGILState_Ensure(); """) out.write("{\nPyObject *py_result=NULL;\n") out.write('PyObject *method_name = PyString_FromString("%s");\n' % self.name) out.write(self.return_type.returned_python_definition()) for arg in self.args: out.write("PyObject *py_%s=NULL;\n" % arg.name) out.write("\n//Obtain python objects for all the args:\n") for arg in self.args: out.write(arg.to_python_object(result = "py_%s" % arg.name, sense='proxied', BORROWED=True)) out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name)) out.write("\n//Now call the method\n") out.write("""PyErr_Clear(); py_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name,""" % self.myclass.class_name) for arg in self.args: out.write("py_%s," % arg.name) ## Sentinal self.error_set = True out.write("""NULL); /** Check for python errors */ if(PyErr_Occurred()) { PyObject *exception_t, *exception, *tb; PyObject *str; char *str_c; char *error_str; enum _error_type *error_type = aff4_get_current_error(&error_str); // Fetch the exception state and convert it to a string: PyErr_Fetch(&exception_t, &exception, &tb); str = PyObject_Repr(exception); str_c = PyString_AsString(str); if(str_c) { strncpy(error_str, str_c, BUFF_SIZE-1); error_str[BUFF_SIZE-1]=0; *error_type = ERuntimeError; } Py_DECREF(str); goto error; } """); for arg in self.args: out.write(arg.python_proxy_post_call()) ## Now convert the python value back to a value out.write(self.return_type.from_python_object('py_result',self.return_type.name, self, context = "self")) out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n"); out.write("PyGILState_Release(gstate);\n") ## Decref all our python objects: for arg in self.args: out.write("if(py_%s) { Py_DECREF(py_%s);}\n" %( arg.name, arg.name)) out.write(self.return_type.return_value('func_return')) if self.error_set: out.write("\nerror:\n") out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n"); ## Decref all our python objects: for arg in self.args: out.write("if(py_%s) { Py_DECREF(py_%s);}\n" % (arg.name, arg.name)) out.write("PyGILState_Release(gstate);\n %s;\n" % self.error_condition()) out.write(" }\n}\n") def error_condition(self): return self.return_type.error_value class StructConstructor(ConstructorMethod): """ A constructor for struct wrappers - basically just allocate memory for the struct. """ def write_definition(self, out): out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name)) out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name) out.write("\nself->base = talloc(self->ctx, %s);\n" % self.class_name) out.write(" return 0;\n}\n\n") def write_destructor(self, out): out.write("""static void %(class_name)s_dealloc(py%(class_name)s *self) { talloc_free(self->ctx); }\n """ % dict(class_name = self.class_name)) class ProxyConstructor(ConstructorMethod): def write_destructor(self, out): out.write("""static void %(class_name)s_dealloc(py%(class_name)s *self) { if(self->base) { // Release the proxied object //Py_DECREF(self->base->proxied); talloc_free(self->ctx); self->base = NULL; } }\n static int %(class_name)s_destructor(void *this) { py%(class_name)s *self = (py%(class_name)s *)this; Py_DECREF(self->base->proxied); return 0; } """ % dict(class_name = self.class_name)) def initialise_attributes(self, out): attributes = self.myclass.module.classes[self.base_class_name].attributes.get_attributes() for definition_class_name, attribute in attributes: out.write(""" { // Converting from %(attribute_name)s PyErr_Clear(); PyObject *py_result = PyObject_GetAttrString(self->base->proxied, "%(name)s"); if(py_result) { %(type)s tmp; %(from_python_object)s; ((%(definition_class_name)s)self->base)->%(name)s = tmp; Py_DECREF(py_result); } PyErr_Clear(); }""" % dict(definition = attribute.definition(), name=attribute.name, attribute_name = attribute.__class__.__name__, type = attribute.type, definition_class_name = definition_class_name, from_python_object = attribute.from_python_object( 'py_result',"tmp", method=self, context = 'self->base'))) def write_constructor_proxy(self, out): ## Get the base_class constructor self.base_cons_method = ProxiedMethod(self.myclass.module.classes[self.base_class_name].constructor, self.myclass) self.base_cons_method._prototype(out) out.write("{\nPyObject *py_result;\n") out.write('PyObject *method_name;') out.write("%(class_name)s this = (%(class_name)s)self;\n" % self.__dict__) out.write("PyGILState_STATE gstate;\ngstate = PyGILState_Ensure();\n") out.write('method_name = PyString_FromString("__class__");\n') for arg in self.base_cons_method.args: out.write("PyObject *py_%s;\n" % arg.name) out.write("\n//Obtain python objects for all the args:\n") for arg in self.base_cons_method.args: out.write(arg.to_python_object(result = "py_%s" % arg.name, sense = 'proxied', BORROWED=True)) out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name)) out.write(""" // Enlarge the object size to accomodate the extended class self = talloc_realloc_size(self, self, sizeof(struct %(base_class_name)s_t)); """ % self.__dict__) out.write("\n//Now call the method\n") out.write("PyErr_Clear();\npy_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name," % self.myclass.class_name) call = '' for arg in self.base_cons_method.args: call += "py_%s," % arg.name ## Sentinal self.error_set = True call += """NULL""" out.write(call + ");\n"); out.write(""" if(!py_result && PyCallable_Check(this->proxied)) { PyErr_Clear(); py_result = PyObject_CallFunctionObjArgs(((%(name)s)self)->proxied, %(call)s); } /** Check for python errors */ if(PyErr_Occurred()) { PyObject *exception_t, *exception, *tb; PyObject *str; char *str_c; char *error_str; enum _error_type *error_type = aff4_get_current_error(&error_str); // Fetch the exception state and convert it to a string: PyErr_Fetch(&exception_t, &exception, &tb); str = PyObject_Repr(exception); str_c = PyString_AsString(str); if(str_c) { strncpy(error_str, str_c, BUFF_SIZE-1); error_str[BUFF_SIZE-1]=0; *error_type = ERuntimeError; } Py_DECREF(str); goto error; } // Take over the proxied object now this->proxied = py_result; """ % dict(name=self.myclass.class_name, call = call)); ## Now we try to populate the C struct slots with proxies of ## the python objects for class_name, attr in self.myclass.module.classes[\ self.base_class_name].attributes.get_attributes(): out.write(""" // Converting %(name)s from proxy: { PyObject *py_result = PyObject_GetAttrString(this->proxied, "%(name)s"); if(py_result) { """ % dict(name = attr.name)) out.write("{ %s " % attr.definition()) out.write(attr.from_python_object("py_result", "((%s)self)->%s" % (class_name, attr.name), self)) out.write("}\nPy_DECREF(py_result);\n}\n}\n") out.write("PyGILState_Release(gstate);\n") out.write("\n\nreturn self;\n") if self.error_set: out.write("error:\n PyGILState_Release(gstate);\ntalloc_free(self); return NULL;\n") out.write("}\n\n") def write_definition(self, out): self.write_constructor_proxy(out) out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) { PyGILState_STATE gstate = PyGILState_Ensure(); """ % dict(method = self.name, class_name = self.class_name)) self.write_local_vars(out) ## Precall preparations for type in self.args: out.write(type.pre_call(self)) ## Make up the call self.call = "talloc_memdup(NULL, &__%(class_name)s, sizeof(__%(class_name)s));\n" % self.__dict__ ## Now call the wrapped function out.write(""" self->base = %(call)s // Take over a copy of the proxied object self->base->proxied = proxied; /* We take a reference to the proxied object, and the proxied base class takes a reference. This way we (the python object) and the proxied C class can be freed independantly and only when both are freed the proxied object is freed. */ //Py_INCREF(proxied); Py_INCREF(proxied); talloc_set_destructor((void*)self->base, %(class_name)s_destructor); """ % self.__dict__) ## Install the handler for the constructor. FIXME - implement ## shortcut bypassing here so if the proxied class is itself a ## python binding to a C class and it does not override ## anything, we call directly into the original C class method ## instead of converting to python, and back. out.write("((%(definition_class_name)s)self->base)->%(name)s = %(func)s;\n" % dict( definition_class_name = self.base_cons_method.definition_class_name, name = self.base_cons_method.name, func = self.base_cons_method.get_name())) ## Now iterate over all our methods and install handlers: for method in self.myclass.methods: out.write(""" if(1 || PyDict_GetItemString(proxied->ob_type->tp_dict, "%(name)s")) { ((%(definition_class_name)s)self->base)->%(name)s = py%(class_name)s_%(name)s; } """ % dict(definition_class_name = method.definition_class_name, name = method.name, class_name = self.myclass.class_name)) ## Now fill in all attributes from the proxied object. Since ## the C struct attribute access is just memory access its ## difficult to trap it and refill attributes dynamically from ## the python object. Therefore for now we just read all ## attributes initially and populate the C struct with them. self.initialise_attributes(out) out.write("\n PyGILState_Release(gstate);\n return 0;\n"); ## Write the error part of the function if self.error_set: out.write("error:\n PyGILState_Release(gstate);\n " + self.error_condition()); out.write("\n}\n\n") class EmptyConstructor(ConstructorMethod): def write_definition(self, out): out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name)) out.write("""return 0;}\n\n""") class ClassGenerator: docstring = '' def __init__(self, class_name, base_class_name, module): self.class_name = class_name self.methods = [] self.module = module self.constructor = EmptyConstructor(class_name, base_class_name, "Con", [], '', myclass = self) self.base_class_name = base_class_name self.attributes = GetattrMethod(self.class_name, self.base_class_name, self) self.modifier = set() self.active = True self.iterator = None def prepare(self): """ This method is called just before we need to write the output and allows us to do any last minute fixups. """ pass def __str__(self): result = "#%s\n" % self.docstring result += "Class %s(%s):\n" % (self.class_name, self.base_class_name) result += " Constructor:%s\n" % self.constructor result += " Attributes:\n%s\n" % self.attributes result += " Methods:\n" for a in self.methods: result += " %s\n" % a.__str__() return result def is_active(self): """ Returns true if this class is active and should be generated """ if not self.active or self.modifier and \ ('PRIVATE' in self.modifier or 'ABSTRACT' in self.modifier): log("%s is not active %s" % (self.class_name, self.modifier)) return False return True def clone(self, new_class_name): """ Creates a clone of this class - usefull when implementing class extensions """ result = ClassGenerator(new_class_name, self.class_name, self.module) result.constructor = self.constructor.clone(new_class_name) result.methods = [ x.clone(new_class_name) for x in self.methods ] result.attributes = self.attributes.clone(new_class_name) return result def add_attribute(self, attr_name, attr_type, modifier): try: if not self.module.classes[attr_type].is_active(): return except KeyError: pass try: ## All attribute references are always borrowed - that ## means we dont want to free them after accessing them type_class = dispatch(attr_name, "BORROWED "+attr_type) except KeyError: log("Unknown attribute type %s for %s.%s" % (attr_type, self.class_name, attr_name)) return type_class.attributes.add(modifier) self.attributes.add_attribute(type_class) def add_constructor(self, method_name, args, return_type, docstring): if method_name.startswith("Con"): self.constructor = ConstructorMethod(self.class_name, self.base_class_name, method_name, args, return_type, myclass = self) self.constructor.docstring = docstring def struct(self,out): out.write("""\ntypedef struct { PyObject_HEAD %(class_name)s base; void *ctx; } py%(class_name)s;\n """ % dict(class_name=self.class_name)) def code(self, out): if not self.constructor: raise RuntimeError("No constructor found for class %s" % self.class_name) self.constructor.write_destructor(out) self.constructor.write_definition(out) if self.attributes: self.attributes.write_definition(out) for m in self.methods: m.write_definition(out) def initialise(self): return "python_wrappers[TOTAL_CLASSES].class_ref = (Object)&__%s;\n" \ "python_wrappers[TOTAL_CLASSES++].python_type = &%s_Type;\n" % ( self.class_name, self.class_name) def PyMethodDef(self, out): out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name) for method in self.methods: method.PyMethodDef(out) out.write(" {NULL} /* Sentinel */\n};\n") def prototypes(self, out): """ Write prototype suitable for .h file """ out.write("""staticforward PyTypeObject %s_Type;\n""" % self.class_name) self.constructor.prototype(out) if self.attributes: self.attributes.prototype(out) for method in self.methods: method.prototype(out) def numeric_protocol_int(self): pass def numeric_protocol_nonzero(self): return """ static int %(class_name)s_nonzero(py%(class_name)s *v) { return v->base != 0; } """ % self.__dict__ def numeric_protocol(self, out): args = {'class':self.class_name} for type, func in [ ('nonzero', self.numeric_protocol_nonzero), ('int', self.numeric_protocol_int) ]: definition = func() if definition: out.write(definition) args[type] = "%s_%s" % (self.class_name,type) else: args[type] = '0' out.write(""" static PyNumberMethods %(class)s_as_number = { (binaryfunc) 0, /*nb_add*/ (binaryfunc) 0, /*nb_subtract*/ (binaryfunc) 0, /*nb_multiply*/ 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ (unaryfunc) 0, /*nb_negative*/ (unaryfunc) 0, /*tp_positive*/ (unaryfunc) 0, /*tp_absolute*/ (inquiry) %(nonzero)s, /*tp_nonzero*/ (unaryfunc) 0, /*nb_invert*/ 0, /*nb_lshift*/ (binaryfunc) 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ 0, /*nb_coerce*/ (unaryfunc) %(int)s, /*nb_int*/ 0, /*nb_long*/ 0, /*nb_float*/ 0, /*nb_oct*/ 0, /*nb_hex*/ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ 0, /* nb_inplace_rshift */ 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ 0, /* nb_floor_divide */ 0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ 0, /* nb_index */ }; """ % args) return "&%(class)s_as_number" % args def PyTypeObject(self, out): args = {'class':self.class_name, 'module': self.module.name, 'iterator': 0, 'iternext': 0, 'tp_str': 0, 'getattr_func': 0, 'docstring': "%s: %s" % (self.class_name, escape_for_string(self.docstring))} if self.attributes: args['getattr_func'] = self.attributes.name args['numeric_protocol'] = self.numeric_protocol(out) if "ITERATOR" in self.modifier: args['iterator'] = "PyObject_SelfIter" args['iternext'] = "py%s_iternext" % self.class_name if "SELF_ITER" in self.modifier: args['iterator'] = 'py%s___iter__' % self.class_name if "TP_STR" in self.modifier: args['tp_str'] = 'py%s___str__' % self.class_name out.write(""" static PyTypeObject %(class)s_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "%(module)s.%(class)s", /* tp_name */ sizeof(py%(class)s), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)%(class)s_dealloc,/* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ %(numeric_protocol)s, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)%(tp_str)s, /* tp_str */ (getattrofunc)%(getattr_func)s, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "%(docstring)s", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)%(iterator)s, /* tp_iter */ (iternextfunc)%(iternext)s,/* tp_iternext */ %(class)s_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)py%(class)s_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; """ % args ) class StructGenerator(ClassGenerator): """ A wrapper generator for structs """ def __init__(self, class_name, module): self.class_name = class_name self.methods = [] self.module = module self.base_class_name = None self.active = False self.modifier = set() self.constructor = None self.attributes = GetattrMethod(self.class_name, self.base_class_name, self) def prepare(self): ## This is needed for late stage initialization - sometimes ## our class_name is not know until now. if not self.constructor: self.constructor = StructConstructor(self.class_name, self.base_class_name, 'Con', [], "void", myclass = self) self.attributes.rename_class_name(self.class_name) for x in self.attributes._attributes: x[1].attributes.add('FOREIGN') def __str__(self): result = "#%s\n" % self.docstring result += "Struct %s:\n" % (self.class_name) result += "%s\n" % self.attributes return result def struct(self, out): out.write("""\ntypedef struct { PyObject_HEAD %(class_name)s *base; void *ctx; } py%(class_name)s;\n """ % dict(class_name=self.class_name)) def initialise(self): return '' class ProxyClassGenerator(ClassGenerator): def __init__(self, class_name, base_class_name, module, *args, **kwargs): ClassGenerator.__init__(self, class_name, base_class_name, module, *args, **kwargs) self.constructor = ProxyConstructor(self.class_name, self.base_class_name, '__init__', [('PyObject *', 'proxied')], 'void', myclass = self) self.module = module self.attributes = ProxiedGetattr(self.class_name, self.base_class_name, self) def initialise(self): return "INIT_CLASS(%(class_name)s);\n" % self.__dict__ + ClassGenerator.initialise(self) def struct(self, out): out.write(""" // The proxied type is an extension of the wrapped type with a pointer // to the proxied PyObject. CLASS(%(class_name)s, %(base_class_name)s) uint32_t magic; PyObject *proxied; END_CLASS VIRTUAL(%(class_name)s, %(base_class_name)s) { } END_VIRTUAL typedef struct { PyObject_HEAD %(class_name)s base; void *ctx; } py%(class_name)s;\n """ % self.__dict__) def PyMethodDef(self, out): out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name) ## For now no methods out.write(" {NULL} /* Sentinel */\n};\n") class parser: class_re = re.compile(r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)") method_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?") enum_start_re = re.compile(r'enum ([0-9A-Z_a-z]+) {') enum_re = re.compile(r'([0-9A-Z_a-z]+) = [^,]+,') enum_end_re = re.compile('}') arg_re = re.compile(r"\s*([0-9A-Z a-z_]+\s+\*?)([0-9A-Za-z_]+),?") constant_re = re.compile(r"#define\s+([A-Z_0-9]+)\s+[^\s]+") struct_re = re.compile(r"([A-Z]+)?\s+(typedef )?struct\s+([A-Z_a-z0-9]+)\s+{") proxy_class_re = re.compile(r"^([A-Z]+)?\s*PROXY_CLASS\(([A-Za-z0-9]+)\)") end_class_re = re.compile("END_CLASS") attribute_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z]+)\s*;") comment_re = re.compile(r"^\s*//") comment_start_re = re.compile(r"/\*+") comment_end_re = re.compile(r"\*+/") blank_line_re = re.compile("\s+") typedef_re = re.compile("typedef ([A-Za-z_0-9]+) +([A-Za-z_0-9]+) *;") bind_re = re.compile("BIND\(([a-zA-Z_0-9]+)\);") current_class = None def __init__(self, module, verbosity=0): self.module = module self.current_comment = '' self.verbosity = verbosity self.state = 'DEFAULT' ## this is a list of objects which are to be bound self.to_bind = [] global DEBUG DEBUG = verbosity io = StringIO.StringIO(""" // Base object CLASS(Object, Obj) END_CLASS """) self.parse_fd(io, '') def add_class(self, class_name, base_class_name, class_type, handler, docstring, modifier): try: self.current_class = self.module.classes[base_class_name].clone(class_name) except (KeyError, AttributeError): log("Base class %s is not defined !!!!" % base_class_name) self.current_class = class_type(class_name, base_class_name, self.module) ## Now add the new class to the module object self.current_class.docstring = docstring self.current_class.modifier = modifier self.module.add_class(self.current_class, handler) def parse_filenames(self, filenames): ## Be quiet for the first pass as many problems will be ## resolved on the second pass anyway. global DEBUG DEBUG = 0 for f in filenames: self._parse(f) DEBUG = self.verbosity log("Second pass: Consolidating definitions") for f in filenames: self._parse(f) def _parse(self, filename): fd = open(filename) self.parse_fd(fd, filename) fd.close() if filename not in self.module.files: self.module.headers += '#include "%s"\n' % filename self.module.files.append(filename) def parse_fd(self, fd, filename): self.current_class = None self.current_comment = '' while 1: line = fd.readline() if not line: break ## Handle binds m= self.bind_re.search(line) if m: print "Will bind %s" % m.group(1) self.to_bind.append(m.group(1)) continue ## Handle enums if self.state == 'DEFAULT': m = self.enum_start_re.search(line) if m: self.state = 'enum' type_dispatcher[m.group(1)] = type_dispatcher['int'] continue elif self.state == 'enum': m = self.enum_re.search(line) if m: self.module.add_constant(m.group(1), 'integer') if '}' in line: self.state = 'DEFAULT' continue ## Handle c++ style comments // m = self.comment_re.match(line) if m: self.current_comment = line[m.end():] while 1: line = fd.readline() m = self.comment_re.match(line) if not m: break self.current_comment += line[m.end():] ## Multiline C style comments m = self.comment_start_re.search(line) if m: line = line[m.end():] while 1: m = self.comment_end_re.search(line) if m: self.current_comment += line[:m.start()] line = fd.readline() break else: self.current_comment += line line = fd.readline() if not line: break ## Handle simple typdefs (as if typedef uint64_t my_fancy_type;) m = self.typedef_re.search(line) if m: ## We basically add a new type as a copy of the old ## type old, new = m.group(1), m.group(2) if old in type_dispatcher: type_dispatcher[new] = type_dispatcher[old] ## Handle constant #define m = self.constant_re.search(line) if m: ## We need to determine if it is a string or integer if re.search('"', line): ## Its a string self.module.add_constant(m.group(1), 'string') else: self.module.add_constant(m.group(1), 'integer') ## Wrap structs m = self.struct_re.search(line) if m: modifier = m.group(1) class_name = m.group(3) base_class_name = None ## Structs may be refered to as a pointer or absolute ## - its the same thing ultimatley. ## We only wrap structures which are explicitely bound if (modifier and 'BOUND' in modifier) or \ class_name in self.to_bind: self.add_class(class_name, base_class_name, StructGenerator, StructWrapper, self.current_comment, modifier) type_dispatcher["%s *" % class_name] = PointerStructWrapper continue m = self.class_re.search(line) if m: ## We need to make a new class now... We basically ## need to build on top of previously declared base ## classes - so we try to find base classes, clone ## them if possible: modifier = m.group(1) class_name = m.group(2) base_class_name = m.group(3) self.add_class(class_name, base_class_name, ClassGenerator, Wrapper, self.current_comment, modifier) type_dispatcher["%s *" % class_name] = PointerWrapper continue ## Make a proxy class for python callbacks m = self.proxy_class_re.search(line) if m: modifier = m.group(1) base_class_name = m.group(2) class_name = "Proxied%s" % base_class_name try: proxied_class = self.module.classes[base_class_name] except KeyError: raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name) self.current_class = ProxyClassGenerator(class_name, base_class_name, self.module) #self.current_class.constructor.args += proxied_class.constructor.args self.current_class.docstring = self.current_comment ## Create proxies for all these methods for method in proxied_class.methods: self.current_class.methods.append(ProxiedMethod(method, self.current_class)) self.module.add_class(self.current_class, Wrapper) ## Make sure that further lines are not interpreted as part of this class. self.current_class = None m = self.method_re.search(line) if self.current_class and m: args = [] method_name = m.group(3) return_type = m.group(1).strip() ## Ignore private methods if return_type.startswith("PRIVATE"): continue ## Now parse the args offset = m.end() while 1: m = self.arg_re.match(line[offset:]) if not m: ## Allow multiline definitions if there is \\ ## at the end of the line if line.strip().endswith("\\"): line = fd.readline() offset = 0 if line: continue break offset += m.end() args.append([m.group(1).strip(), m.group(2).strip()]) if return_type == self.current_class.class_name and \ method_name.startswith("Con"): self.current_class.add_constructor(method_name, args, return_type, self.current_comment) else: self.current_class.add_method(method_name, args, return_type, self.current_comment) m = self.attribute_re.search(line) if self.current_class and m: type = m.group(1) name = m.group(2) self.current_class.add_attribute(name, type) m = self.end_class_re.search(line) if m: ## Just clear the current class context self.current_class = None ## If this is a shadow file we do not include it from the ## main module. A shadow file is a way for us to redefine ## other C constructs which will be imported from an ## external header file so we can bind them slightly ## differently. if "this is a shadow file" in self.current_comment \ and filename not in self.module.files: self.module.files.append(filename) ## We only care about comment immediately above methods ## etc as we take them to be documentation. If we get here ## the comment is not above anything we care about - so we ## clear it: self.current_comment = '' def write(self, out): self.module.write(out) import lexer class EnumConstructor(ConstructorMethod): def write_destructor(self, out): out.write("""static void %(class_name)s_dealloc(py%(class_name)s *self) { Py_DECREF(self->value); PyObject_Del(self); }\n """ % dict(class_name = self.class_name)) def write_definition(self, out): self.myclass.modifier.add("TP_STR") self._prototype(out) out.write("""{ static char *kwlist[] = {"value", NULL}; if(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &self->value)) goto error; Py_INCREF(self->value); return 0; error: return -1; } static PyObject *py%(class_name)s___str__(py%(class_name)s *self) { PyObject *result = PyDict_GetItem(%(class_name)s_rev_lookup, self->value); if(result) { Py_INCREF(result); } else { result = PyObject_Str(self->value); } return result; } """ % self.__dict__) class Enum(StructGenerator): def __init__(self, name, module): StructGenerator.__init__(self, name, module) self.values = [] self.name = name self.attributes = None self.active = True def prepare(self): self.constructor = EnumConstructor(self.class_name, self.base_class_name, 'Con', [], "void", myclass = self) StructGenerator.prepare(self) def __str__(self): result = "Enum %s:\n" % (self.name) for attr in self.values: result += " %s\n" % attr.__str__() return result def struct(self,out): out.write("""\ntypedef struct { PyObject_HEAD PyObject *value; } py%(class_name)s;\n static PyObject *%(class_name)s_Dict_lookup; static PyObject *%(class_name)s_rev_lookup; """ % dict(class_name=self.class_name)) def PyMethodDef(self, out): out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name) out.write(" {NULL} /* Sentinel */\n};\n") def numeric_protocol_nonzero(self): pass def numeric_protocol_int(self): return """ static PyObject *%(class_name)s_int(py%(class_name)s *self) { Py_INCREF(self->value); return self->value; } """ % self.__dict__ def initialise(self): result = """ %(class_name)s_Dict_lookup = PyDict_New(); %(class_name)s_rev_lookup = PyDict_New(); """ % self.__dict__ if self.values: result += "{ PyObject *tmp, *tmp2;\n" for attr in self.values: result += ''' tmp = PyLong_FromLong(%(value)s); tmp2 = PyString_FromString("%(value)s"); PyDict_SetItem(%(class_name)s_Dict_lookup, tmp2, tmp); PyDict_SetItem(%(class_name)s_rev_lookup, tmp, tmp2); Py_DECREF(tmp); Py_DECREF(tmp2); ''' % dict(value = attr, class_name=self.class_name) result += "};\n" return result class EnumType(Integer): buildstr = 'i' def __init__(self, name, type): Integer.__init__(self, name, type) self.type = type def to_python_object(self, name=None, result='py_result', **kw): name = name or self.name return """PyErr_Clear(); %s = PyObject_CallMethod(g_module, "%s", "K", (uint64_t)%s); """ % (result, self.type, name) def pre_call(self, method): method.error_set = True return """ // Check if the integer passed is actually a valid member of the enum // Enum value of 0 is always allowed if(%(name)s) { PyObject *py_%(name)s = PyLong_FromLong(%(name)s); PyObject *tmp = PyDict_GetItem(%(type)s_rev_lookup, py_%(name)s); Py_DECREF(py_%(name)s); if(!tmp) { PyErr_Format(PyExc_RuntimeError, "value %%lu is not valid for Enum %(type)s of arg '%(name)s'", (unsigned long)%(name)s); goto error; } Py_DECREF(tmp); } """ % self.__dict__ class HeaderParser(lexer.SelfFeederMixIn): tokens = [ [ 'INITIAL', r'#define\s+', 'PUSH_STATE', 'DEFINE' ], [ 'DEFINE', r'([A-Za-z_0-9]+)\s+[^\n]+', 'DEFINE,POP_STATE', None ], [ 'DEFINE', r'\n', 'POP_STATE', None], ## Recognize ansi c comments [ '.', r'/\*(.)', 'PUSH_STATE', 'COMMENT' ], [ 'COMMENT', r'(.+?)\*/\s+', 'COMMENT_END,POP_STATE', None], [ 'COMMENT', r'(.+)', 'COMMENT', None], ## And c++ comments [ '.', r'//([^\n]+)', 'COMMENT', None], ## An empty line clears the current comment [ '.', r'\r?\n\r?\n', 'CLEAR_COMMENT', None], ## Ignore whitespace [ '.', r'\s+', 'SPACE', None ], [ '.', r'\\\n', 'SPACE', None ], ## Recognize CLASS() definitions [ 'INITIAL', r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)", 'PUSH_STATE,CLASS_START', 'CLASS'], [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT|PRIVATE)?([0-9A-Z_a-z ]+( |\*))METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?", "PUSH_STATE,METHOD_START", "METHOD"], [ 'METHOD', r"\s*([0-9A-Z a-z_]+\s+\*?\*?)([0-9A-Za-z_]+),?", "METHOD_ARG", None ], [ 'METHOD', r'\);', 'POP_STATE,METHOD_END', None], [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT)?([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;", 'CLASS_ATTRIBUTE', None], [ 'CLASS', "END_CLASS", 'END_CLASS,POP_STATE', None], ## Recognize struct definitions (With name) [ 'INITIAL', "([A-Z_a-z0-9 ]+)?struct\s+([A-Z_a-z0-9]+)\s+{", 'PUSH_STATE,STRUCT_START', 'STRUCT'], ## Without name (using typedef) [ 'INITIAL', "typedef\s+struct\s+{", 'PUSH_STATE,TYPEDEF_STRUCT_START', 'STRUCT'], [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;", 'STRUCT_ATTRIBUTE', None], [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+)\*\s+([A-Z_a-z0-9]+)\s*;", 'STRUCT_ATTRIBUTE_PTR', None], ## Struct ended with typedef [ 'STRUCT', '}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEF_STRUCT_END', None], [ 'STRUCT', '}', 'POP_STATE,STRUCT_END', None], ## Handle recursive struct or union definition (At the moment ## we cant handle them at all) [ '(RECURSIVE_)?STRUCT', '(struct|union)\s+([_A-Za-z0-9]+)?\s*{', 'PUSH_STATE', 'RECURSIVE_STRUCT'], [ 'RECURSIVE_STRUCT', '}\s+[0-9A-Za-z]+', 'POP_STATE', None], ## Process enums (2 forms - named and typedefed) [ 'INITIAL', r'enum\s+([0-9A-Za-z_]+)\s+{', 'PUSH_STATE,ENUM_START', 'ENUM' ], ## Unnamed [ 'INITIAL', r'typedef\s+enum\s+{', 'PUSH_STATE,TYPEDEF_ENUM_START', 'ENUM' ], [ 'ENUM', r'([0-9A-Za-z_]+)\s+=[^\n]+', 'ENUM_VALUE', None], ## Typedefed ending [ 'ENUM', r'}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEFED_ENUM_END', None], [ 'ENUM', r'}', 'POP_STATE,ENUM_END', None], [ 'INITIAL', r'BIND_STRUCT\(([0-9A-Za-z_ \*]+)\)', 'BIND_STRUCT', None], ## A simple typedef of one type for another type: [ 'INITIAL', r"typedef ([A-Za-z_0-9]+) +([^;]+);", 'SIMPLE_TYPEDEF', None], ## Handle proxied directives [ 'INITIAL', r"PROXY_CLASS\(([A-Za-z0-9]+)\);", 'PROXY_CLASS', None], ] def __init__(self, name, verbose = 1): self.module = Module(name) lexer.SelfFeederMixIn.__init__(self, verbose = 0) io = StringIO.StringIO(""" // Base object CLASS(Object, Obj) END_CLASS """) self.parse_fd(io) current_comment = '' def COMMENT(self, t, m): self.current_comment += m.group(1) + "\n" def COMMENT_END(self, t, m): self.current_comment += m.group(1) def CLEAR_COMMENT(self, t, m): self.current_comment = '' def DEFINE(self, t, m): line = m.group(0) line = line.split('/*')[0] if '"' in line: type = 'string' else: type = 'integer' name = m.group(1).strip() if len(name)>3 and name[0]!='_' and name==name.upper(): self.module.add_constant(name, type) current_class = None def CLASS_START(self, t, m): class_name = m.group(2).strip() base_class_name = m.group(3).strip() try: self.current_class = self.module.classes[base_class_name].clone(class_name) except (KeyError, AttributeError): log("Base class %s is not defined !!!!" % base_class_name) self.current_class = ClassGenerator(class_name, base_class_name, self.module) self.current_class.docstring = self.current_comment self.current_class.modifier.add(m.group(1)) self.module.add_class(self.current_class, Wrapper) type_dispatcher["%s *" % class_name] = PointerWrapper current_method = None def METHOD_START(self, t, m): return_type = m.group(2).strip() method_name = m.group(5).strip() modifier = m.group(1) or '' if 'PRIVATE' in modifier: return ## Is it a regular method or a constructor? self.current_method = Method if return_type == self.current_class.class_name and \ method_name.startswith("Con"): self.current_method = ConstructorMethod elif method_name == 'iternext': self.current_method = IteratorMethod self.current_class.modifier.add("ITERATOR") elif method_name == '__iter__': self.current_method = SelfIteratorMethod self.current_class.modifier.add("SELF_ITER") elif method_name == '__str__': self.current_class.modifier.add("TP_STR") self.current_method = self.current_method(self.current_class.class_name, self.current_class.base_class_name, method_name, [], return_type, myclass = self.current_class) self.current_method.docstring = self.current_comment self.current_method.modifier = modifier def METHOD_ARG(self, t, m): name = m.group(2).strip() type = m.group(1).strip() if self.current_method: self.current_method.add_arg(type, name) def METHOD_END(self, t, m): if not self.current_method: return if isinstance(self.current_method, ConstructorMethod): self.current_class.constructor = self.current_method else: found = False for i in range(len(self.current_class.methods)): ## Try to replace existing methods with this new method method = self.current_class.methods[i] if method.name == self.current_method.name: self.current_class.methods[i] = self.current_method self.current_method = None return ## Method does not exist, just add to the end self.current_class.methods.append(self.current_method) self.current_method = None def CLASS_ATTRIBUTE(self, t, m): modifier = m.group(1) or '' type = m.group(2).strip() name = m.group(3).strip() self.current_class.add_attribute(name, type, modifier) def END_CLASS(self, t, m): self.current_class = None current_struct = None def STRUCT_START(self, t, m): self.current_struct = StructGenerator(m.group(2).strip(), self.module) self.current_struct.docstring = self.current_comment self.current_struct.modifier.add(m.group(1)) def TYPEDEF_STRUCT_START(self, t, m): self.current_struct = StructGenerator(None, self.module) self.current_struct.docstring = self.current_comment def STRUCT_ATTRIBUTE(self, t, m): name = m.group(2).strip() type = m.group(1).strip() self.current_struct.add_attribute(name, type, '') def STRUCT_ATTRIBUTE_PTR(self, t, m): type = "%s *" % m.group(1).strip() name = m.group(2).strip() self.current_struct.add_attribute(name, type, '') def STRUCT_END(self, t, m): self.module.add_class(self.current_struct, StructWrapper) type_dispatcher["%s *" % self.current_struct.class_name] = PointerStructWrapper self.current_struct = None def TYPEDEF_STRUCT_END(self, t, m): self.current_struct.class_name = m.group(1).strip() self.STRUCT_END(t, m) current_enum = None def ENUM_START(self, t, m): self.current_enum = Enum(m.group(1).strip(), self.module) def TYPEDEF_ENUM_START(self, t, m): self.current_enum = Enum(None, self.module) def ENUM_VALUE(self, t, m): self.current_enum.values.append(m.group(1).strip()) def ENUM_END(self, t, m): self.module.classes[self.current_enum.name] = self.current_enum ## For now we just treat enums as an integer, and also add ## them to the constant table. In future it would be nice to ## have them as a proper python object so we can override ## __str__ and __int__. for attr in self.current_enum.values: self.module.add_constant(attr, 'integer') #type_dispatcher[self.current_enum.name] = Integer type_dispatcher[self.current_enum.name] = EnumType self.current_enum = None def TYPEDEFED_ENUM_END(self, t, m): self.current_enum.name = self.current_enum.class_name = m.group(1) self.ENUM_END(t, m) def BIND_STRUCT(self, t, m): self.module.classes[m.group(1)].active = True def SIMPLE_TYPEDEF(self, t, m): ## We basically add a new type as a copy of the old ## type old, new = m.group(1).strip(), m.group(2).strip() if old in type_dispatcher: type_dispatcher[new] = type_dispatcher[old] def PROXY_CLASS(self, t, m): base_class_name = m.group(1).strip() class_name = "Proxied%s" % base_class_name try: proxied_class = self.module.classes[base_class_name] except KeyError: raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name) current_class = ProxyClassGenerator(class_name, base_class_name, self.module) #self.current_class.constructor.args += proxied_class.constructor.args current_class.docstring = self.current_comment ## Create proxies for all these methods for method in proxied_class.methods: current_class.methods.append(ProxiedMethod(method, current_class)) self.module.add_class(current_class, Wrapper) def parse_filenames(self, filenames): for f in filenames: self._parse(f) ## Second pass for f in filenames: self._parse(f) def _parse(self, filename): fd = open(filename) self.parse_fd(fd) fd.close() if filename not in self.module.files: self.module.headers += '#include "%s"\n' % filename self.module.files.append(filename) def write(self, out): self.module.write(out) if __name__ == '__main__': p = HeaderParser('pyaff4', verbose = 1) for arg in sys.argv[1:]: p.parse_fd(open(arg)) log("second parse") for arg in sys.argv[1:]: p.parse_fd(open(arg)) pdb.set_trace() p.write(sys.stdout) # p = parser(Module("pyaff4")) # for arg in sys.argv[1:]: # p.parse(arg) # log("second parse") # p.parse(arg) # p.write(sys.stdout) reglookup+git/include/lru_cache.h000664 001750 001750 00000005065 12566244653 020232 0ustar00sophiesophie000000 000000 /* * 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 * 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$ */ /** * @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 #include #include #include #include #include #include #include #include "compat.h" struct lru_cache_element; typedef struct lru_cache_element lru_cache_element; struct lru_cache_element { void* index; uint32_t index_len; void* data; lru_cache_element* next; lru_cache_element* older; lru_cache_element* newer; }; /** XXX: document this. */ typedef struct _lru_cache { uint32_t secret; uint32_t num_keys; uint32_t num_buckets; uint32_t max_keys; lru_cache_element* oldest; lru_cache_element* newest; lru_cache_element** table; bool talloc_data; } lru_cache; /** * XXX: finish documenting. */ _EXPORT() lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret); /** * XXX: finish documenting. */ _EXPORT() lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, uint32_t secret, bool talloc_data); /** * XXX: finish documenting. */ _EXPORT() void lru_cache_destroy(lru_cache* ht); /** * XXX: finish documenting. */ _EXPORT() bool lru_cache_update(lru_cache* ht, const void* index, uint32_t index_len, void* data); /** * XXX: finish documenting. * * @return A pointer to data previously stored at index. * If no data was found at index, NULL is returned. */ _EXPORT() void* lru_cache_find(lru_cache* ht, const void* index, uint32_t index_len); /** * 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. */ _EXPORT() bool lru_cache_remove(lru_cache* ht, const void* index, uint32_t index_len); #endif reglookup+git/doc/devel/winntreg.txt000664 001750 001750 00000024274 12566244653 020756 0ustar00sophiesophie000000 000000 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. reglookup+git/src/000775 001750 001750 00000000000 12566244653 015272 5ustar00sophiesophie000000 000000 reglookup+git/doc/devel/README000664 001750 001750 00000000452 12566244653 017230 0ustar00sophiesophie000000 000000 This directory contains technical information on the registry format, RegLookup design goals and programmer's API usage information. For information on how to use the RegLookup tools, refer to the doc directory and man pages included in the main release distribution or Subversion trunk directory. reglookup+git/doc/devel/mingw-build-OBSOLETE.txt000664 001750 001750 00000005513 12566244653 022544 0ustar00sophiesophie000000 000000 Cross-compiling RegLookup to Windows with MinGW =============================================== MinGW can be used to compile Windows binaries from UNIX environments. The following instructions outline the steps required to build reglookup.exe and reglookup-recover.exe. This process is experimental and Windows binaries have not been well tested. You have been warned. Prerequisites ------------- - Before you start, ensure you have MinGW installed. Under Debian, install the `mingw32' package. - Download pre-compiled libiconv packages from here: http://gnuwin32.sourceforge.net/packages/libiconv.htm You will need to download the "Binaries" and "Developer files" packages, whose files are named libiconv-VERSION-bin.zip and libiconv-VERSION-lib.zip respectively. - Unpack both zip files into a designated top-level directory. Suggested commands: $ mkdir /usr/local/src/libiconv-VERSION-bin /usr/local/src/libiconv-VERSION-lib $ cd /usr/local/src/libiconv-VERSION-bin $ unzip .../path/to/libiconv-VERSION-bin.zip $ cd /usr/local/src/libiconv-VERSION-lib $ unzip .../path/to/libiconv-VERSION-lib.zip Building -------- Review the top level RegLookup Makefile to ensure the settings match your environment. Find the conditional block which looks like: ################################################################################ # MinGW cross-compiling build settings ifdef BUILD_MINGW ## These may need to be changed CC=i586-mingw32msvc-cc LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib ## These probably do not need to be changed BIN_EXT=.exe INC:=$(INC) -I$(LIBICONV_PATH)/include EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a endif ################################################################################ If either the CC or LIBICONV_PATH settings are incorrect for your system, either update the Makefile, or override these options at build time when you run make. For instance, the above settings in the Makefile are correct, you can execute the build by running: $ make BUILD_MINGW=1 Alternatively, you may override the variables above with: $ make BUILD_MINGW=1 CC=my-mingw-binary LIBICONV_PATH=.../path/to/libiconv-VERSION-lib Once the build is complete, you'll find the .exe files under the build/bin directory. Installation ------------ Naturally, there is no install Makefile target for the MinGW build process, since we aren't installing on the local system. To install these binaries on a Windows machine, simply copy over the reglookup.exe and reglookup-recover.exe files from the build/bin directory to the desired host. In addition, you will need to install the libiconv2.dll file on that host (either in the same directory as the reglookup executables, or somewhere in the DLL search path). This file is available in the libiconv-VERSION-bin.zip file you downloaded earlier, under the `bin' subdirectory. reglookup+git/python/experimental/000775 001750 001750 00000000000 12566244653 020521 5ustar00sophiesophie000000 000000 reglookup+git/lib/regfi.c000664 001750 001750 00000326231 12566244653 016520 0ustar00sophiesophie000000 000000 /* * Copyright (C) 2005-2011,2015 Timothy D. Morgan * Copyright (C) 2005 Gerald (Jerry) Carter * * 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$ */ /** * @file * * Windows NT (and later) read-only registry library * * See @ref regfi.h for more information. * * 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" /* Library version can be overridden at build time */ #ifndef REGFI_VERSION #define REGFI_VERSION "trunk" #endif /* Registry types mapping */ const unsigned int regfi_num_reg_types = 12; 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", "UTF-8", "UTF-16LE"}; /* Ensures regfi_init runs only once */ static pthread_once_t regfi_init_once = PTHREAD_ONCE_INIT; /****************************************************************************** ******************************************************************************/ const char* regfi_version() { return REGFI_VERSION; } /****************************************************************************** ******************************************************************************/ void regfi_log_free(void* ptr) { REGFI_LOG* log_info = (REGFI_LOG*)ptr; if(log_info->messages != NULL) free(log_info->messages); talloc_free(log_info); } /****************************************************************************** ******************************************************************************/ void regfi_init() { int err; if((err = pthread_key_create(®fi_log_key, regfi_log_free)) != 0) fprintf(stderr, "ERROR: key_create: %s\n", strerror(err)); errno = err; } /****************************************************************************** ******************************************************************************/ REGFI_LOG* regfi_log_new() { int err; REGFI_LOG* log_info = talloc(NULL, REGFI_LOG); if(log_info == NULL) return NULL; log_info->msg_mask = REGFI_DEFAULT_LOG_MASK; log_info->messages = NULL; pthread_once(®fi_init_once, regfi_init); if((err = pthread_setspecific(regfi_log_key, log_info)) != 0) { fprintf(stderr, "ERROR: setspecific: %s\n", strerror(err)); goto fail; } return log_info; fail: talloc_free(log_info); errno = err; return NULL; } /****************************************************************************** ******************************************************************************/ void regfi_log_add(uint16_t msg_type, const char* fmt, ...) { /* XXX: Switch internal storage over to a linked list or stack. * Then add a regfi_log_get function that returns the list in some * convenient, user-friendly data structure. regfi_log_get_str should * stick around and will simply smush the list into a big string when * it's called, rather than having messages smushed when they're first * written to the log. */ uint32_t buf_size, buf_used; char* new_msg; REGFI_LOG* log_info; va_list args; log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key); if(log_info == NULL && (log_info = regfi_log_new()) == NULL) return; if((log_info->msg_mask & msg_type) == 0) return; if(log_info->messages == NULL) buf_used = 0; else buf_used = strlen(log_info->messages); buf_size = buf_used+strlen(fmt)+160; new_msg = realloc(log_info->messages, buf_size); if(new_msg == NULL) /* XXX: should we report this? */ return; switch (msg_type) { case REGFI_LOG_INFO: strcpy(new_msg+buf_used, "INFO: "); buf_used += 6; break; case REGFI_LOG_WARN: strcpy(new_msg+buf_used, "WARN: "); buf_used += 6; break; case REGFI_LOG_ERROR: strcpy(new_msg+buf_used, "ERROR: "); buf_used += 7; break; } va_start(args, fmt); vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args); va_end(args); strncat(new_msg, "\n", buf_size-1); log_info->messages = new_msg; } /****************************************************************************** ******************************************************************************/ char* regfi_log_get_str() { char* ret_val; REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key); if(log_info == NULL && (log_info = regfi_log_new()) == NULL) return NULL; ret_val = log_info->messages; log_info->messages = NULL; return ret_val; } /****************************************************************************** ******************************************************************************/ bool regfi_log_set_mask(uint16_t msg_mask) { REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key); if(log_info == NULL && (log_info = regfi_log_new()) == NULL) { return false; } log_info->msg_mask = msg_mask; return true; } /****************************************************************************** * 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) return "KEY"; if(val >= regfi_num_reg_types) return NULL; return regfi_type_names[val]; } /****************************************************************************** * Returns -1 on error *****************************************************************************/ int regfi_type_str2val(const char* str) { int i; if(strcmp("KEY", str) == 0) return REG_KEY; for(i=0; i < regfi_num_reg_types; i++) if (strcmp(regfi_type_names[i], str) == 0) return i; if(strcmp("DWORD_LE", str) == 0) return REG_DWORD_LE; return -1; } /* Security descriptor formatting functions */ const char* regfi_ace_type2str(uint8_t type) { static const char* map[7] = {"ALLOW", "DENY", "AUDIT", "ALARM", "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"}; if(type < 7) return map[type]; else /* XXX: would be nice to return the unknown integer value. * However, as it is a const string, it can't be free()ed later on, * so that would need to change. */ return "UNKNOWN"; } /* XXX: need a better reference on the meaning of each flag. */ /* For more info, see: * http://msdn2.microsoft.com/en-us/library/aa772242.aspx */ char* regfi_ace_flags2str(uint8_t flags) { static const char* flag_map[32] = { "OI", /* Object Inherit */ "CI", /* Container Inherit */ "NP", /* Non-Propagate */ "IO", /* Inherit Only */ "IA", /* Inherited ACE */ NULL, NULL, NULL, }; char* ret_val = malloc(35*sizeof(char)); char* fo = ret_val; uint32_t i; uint8_t f; if(ret_val == NULL) return NULL; fo[0] = '\0'; if (!flags) return ret_val; for(i=0; i < 8; i++) { f = (1<num_aces && !failed; i++) { sid_str = winsec_sid2str(acl->aces[i]->trustee); type_str = regfi_ace_type2str(acl->aces[i]->type); perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask); flags_str = regfi_ace_flags2str(acl->aces[i]->flags); if(flags_str != NULL && perms_str != NULL && type_str != NULL && sid_str != NULL) { /* XXX: this is slow */ extra = strlen(sid_str) + strlen(type_str) + strlen(perms_str) + strlen(flags_str) + 5; tmp_val = realloc(ret_val, size+extra); if(tmp_val == NULL) { free(ret_val); ret_val = NULL; failed = true; } else { ret_val = tmp_val; size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s", ace_delim,sid_str, field_delim,type_str, field_delim,perms_str, field_delim,flags_str); ace_delim = "|"; } } else failed = true; if(sid_str != NULL) free(sid_str); if(sid_str != NULL) free(perms_str); if(sid_str != NULL) free(flags_str); } return ret_val; } char* regfi_get_sacl(WINSEC_DESC *sec_desc) { if (sec_desc->sacl) return regfi_get_acl(sec_desc->sacl); else return NULL; } char* regfi_get_dacl(WINSEC_DESC *sec_desc) { if (sec_desc->dacl) return regfi_get_acl(sec_desc->dacl); else return NULL; } char* regfi_get_owner(WINSEC_DESC *sec_desc) { return winsec_sid2str(sec_desc->owner_sid); } char* regfi_get_group(WINSEC_DESC *sec_desc) { return winsec_sid2str(sec_desc->grp_sid); } bool regfi_read_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context) { int lock_ret = pthread_rwlock_rdlock(lock); if(lock_ret != 0) { regfi_log_add(REGFI_LOG_ERROR, "Error obtaining read lock in" "%s due to: %s\n", context, strerror(lock_ret)); return false; } return true; } bool regfi_write_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context) { int lock_ret = pthread_rwlock_wrlock(lock); if(lock_ret != 0) { regfi_log_add(REGFI_LOG_ERROR, "Error obtaining write lock in" "%s due to: %s\n", context, strerror(lock_ret)); return false; } return true; } bool regfi_rw_unlock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context) { int lock_ret = pthread_rwlock_unlock(lock); if(lock_ret != 0) { regfi_log_add(REGFI_LOG_ERROR, "Error releasing lock in" "%s due to: %s\n", context, strerror(lock_ret)); return false; } return true; } bool regfi_lock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context) { int lock_ret = pthread_mutex_lock(lock); if(lock_ret != 0) { regfi_log_add(REGFI_LOG_ERROR, "Error obtaining mutex lock in" "%s due to: %s\n", context, strerror(lock_ret)); return false; } return true; } bool regfi_unlock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context) { int lock_ret = pthread_mutex_unlock(lock); if(lock_ret != 0) { regfi_log_add(REGFI_LOG_ERROR, "Error releasing mutex lock in" "%s due to: %s\n", context, strerror(lock_ret)); return false; } return true; } int64_t regfi_raw_seek(REGFI_RAW_FILE* self, uint64_t offset, int whence) { if(sizeof(off_t) == 4 && offset > 2147483647) { errno = EOVERFLOW; return -1; } return lseek(*(int*)self->state, offset, whence); } ssize_t regfi_raw_read(REGFI_RAW_FILE* self, void* buf, size_t count) { return read(*(int*)self->state, buf, count); } /***************************************************************************** * Convenience function to wrap up the ugly callback stuff *****************************************************************************/ uint64_t regfi_seek(REGFI_RAW_FILE* file_cb, uint64_t offset, int whence) { return file_cb->seek(file_cb, offset, whence); } /***************************************************************************** * This function is just like read(2), except that it continues to * re-try reading from the file descriptor if EINTR or EAGAIN is received. * regfi_read will attempt to read length bytes from the file and write them to * buf. * * On success, 0 is returned. Upon failure, an errno code is returned. * * The number of bytes successfully read is returned through the length * parameter by reference. If both the return value and length parameter are * returned as 0, then EOF was encountered immediately *****************************************************************************/ uint32_t regfi_read(REGFI_RAW_FILE* file_cb, uint8_t* buf, uint32_t* length) { uint32_t rsize = 0; uint32_t rret = 0; do { rret = file_cb->read(file_cb, buf + rsize, *length - rsize); if(rret > 0) rsize += rret; }while(*length - rsize > 0 && (rret > 0 || (rret == -1 && (errno == EAGAIN || errno == EINTR)))); *length = rsize; if (rret == -1 && errno != EINTR && errno != EAGAIN) return errno; return 0; } /***************************************************************************** * *****************************************************************************/ bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset, uint8_t* hdr, uint32_t hdr_len, uint32_t* cell_length, bool* unalloc) { uint32_t length; int32_t raw_length; uint8_t tmp[4]; if(regfi_seek(file_cb, offset, SEEK_SET) == -1) return false; length = 4; if((regfi_read(file_cb, tmp, &length) != 0) || length != 4) return false; raw_length = IVALS(tmp, 0); if(raw_length < 0) { (*cell_length) = raw_length*(-1); (*unalloc) = false; } else { (*cell_length) = raw_length; (*unalloc) = true; } if(*cell_length - 4 < hdr_len) return false; if(hdr_len > 0) { length = hdr_len; if((regfi_read(file_cb, hdr, &length) != 0) || length != hdr_len) return false; } return true; } /****************************************************************************** * 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_t voffset) { if(!hbin) return false; if((voffset > hbin->first_hbin_off) && (voffset < (hbin->first_hbin_off + hbin->block_size))) return true; return false; } /****************************************************************************** * 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_t offset) { 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_t offset, uint32_t num_keys, uint32_t max_size, bool strict) { REGFI_SUBKEY_LIST* ret_val; ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, REGFI_MAX_SUBKEY_DEPTH); if(ret_val == NULL) { regfi_log_add(REGFI_LOG_WARN, "Failed to load subkey list at" " offset 0x%.8X.", offset); return NULL; } if(num_keys != ret_val->num_keys) { /* Not sure which should be authoritative, the number from the * NK record, or the number in the subkey list. Just emit a warning for * now if they don't match. */ regfi_log_add(REGFI_LOG_WARN, "Number of subkeys listed in parent" " (%d) did not match number found in subkey list/tree (%d)" " while parsing subkey list/tree at offset 0x%.8X.", num_keys, ret_val->num_keys, offset); } return ret_val; } /****************************************************************************** ******************************************************************************/ 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; uint32_t i, num_sublists, off; int32_t sublist_maxsize; if(depth_left == 0) { regfi_log_add(REGFI_LOG_WARN, "Maximum depth reached" " while parsing subkey list/tree at offset 0x%.8X.", offset); return NULL; } ret_val = regfi_parse_subkeylist(file, offset, max_size, strict); if(ret_val == NULL) return NULL; if(ret_val->recursive_type) { num_sublists = ret_val->num_children; sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists * sizeof(REGFI_SUBKEY_LIST*)); for(i=0; i < num_sublists; i++) { off = ret_val->elements[i].offset + REGFI_REGF_SIZE; sublist_maxsize = regfi_calc_maxsize(file, off); if(sublist_maxsize < 0) sublists[i] = NULL; else sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, strict, depth_left-1); } talloc_free(ret_val); return regfi_merge_subkeylists(num_sublists, sublists, strict); } return ret_val; } /****************************************************************************** ******************************************************************************/ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_SUBKEY_LIST* ret_val = NULL; 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; if(!regfi_lock(file, &file->cb_lock, "regfi_parse_subkeylist")) goto fail; if(!regfi_parse_cell(file->cb, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while " "parsing subkey-list at offset 0x%.8X.", offset); goto fail_locked; } if(cell_length > max_size) { regfi_log_add(REGFI_LOG_WARN, "Cell size longer than max_size" " while parsing subkey-list at offset 0x%.8X.", offset); if(strict) goto fail_locked; cell_length = max_size & 0xFFFFFFF8; } recursive_type = false; if(buf[0] == 'r' && buf[1] == 'i') { recursive_type = true; elem_size = sizeof(uint32_t); } else if(buf[0] == 'l' && buf[1] == 'i') { elem_size = sizeof(uint32_t); } else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h')) elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM); else { regfi_log_add(REGFI_LOG_ERROR, "Unknown magic number" " (0x%.2X, 0x%.2X) encountered while parsing" " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset); goto fail_locked; } ret_val = talloc(NULL, REGFI_SUBKEY_LIST); if(ret_val == NULL) goto fail_locked; ret_val->offset = offset; ret_val->cell_size = cell_length; ret_val->magic[0] = buf[0]; ret_val->magic[1] = buf[1]; ret_val->recursive_type = recursive_type; ret_val->num_children = SVAL(buf, 0x2); if(!recursive_type) 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_t) < length) { regfi_log_add(REGFI_LOG_WARN, "Number of elements too large for" " cell while parsing subkey-list at offset 0x%.8X.", offset); if(strict) goto fail_locked; length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t); } ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, ret_val->num_children); if(ret_val->elements == NULL) goto fail_locked; elements = (uint8_t*)malloc(length); if(elements == NULL) goto fail_locked; read_len = length; if(regfi_read(file->cb, elements, &read_len) != 0 || read_len!=length) goto fail_locked; if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist")) goto fail; if(elem_size == sizeof(uint32_t)) { for (i=0; i < ret_val->num_children; i++) { ret_val->elements[i].offset = IVAL(elements, i*elem_size); ret_val->elements[i].hash = 0; } } else { for (i=0; i < ret_val->num_children; i++) { ret_val->elements[i].offset = IVAL(elements, i*elem_size); ret_val->elements[i].hash = IVAL(elements, i*elem_size+4); } } free(elements); return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist"); fail: if(elements != NULL) free(elements); talloc_free(ret_val); return NULL; } /******************************************************************* *******************************************************************/ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, REGFI_SUBKEY_LIST** lists, bool strict) { uint32_t i,j,k; REGFI_SUBKEY_LIST* ret_val; if(lists == NULL) return NULL; ret_val = talloc(NULL, REGFI_SUBKEY_LIST); if(ret_val == NULL) return NULL; /* Obtain total number of elements */ ret_val->num_keys = 0; for(i=0; i < num_lists; i++) { if(lists[i] != NULL) ret_val->num_keys += lists[i]->num_children; } ret_val->num_children = ret_val->num_keys; if(ret_val->num_keys > 0) { ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, ret_val->num_keys); k=0; if(ret_val->elements != NULL) { for(i=0; i < num_lists; i++) { if(lists[i] != NULL) { for(j=0; j < lists[i]->num_keys; j++) { ret_val->elements[k].hash = lists[i]->elements[j].hash; ret_val->elements[k++].offset = lists[i]->elements[j].offset; } } } } } for(i=0; i < num_lists; i++) talloc_free(lists[i]); free(lists); return ret_val; } /****************************************************************************** * ******************************************************************************/ REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_SK* ret_val = NULL; uint8_t* sec_desc_buf = NULL; uint32_t cell_length, length; uint8_t sk_header[REGFI_SK_MIN_LENGTH]; bool unalloc = false; if(!regfi_lock(file, &file->cb_lock, "regfi_parse_sk")) goto fail; if(!regfi_parse_cell(file->cb, offset, sk_header, REGFI_SK_MIN_LENGTH, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse SK record cell" " at offset 0x%.8X.", offset); goto fail_locked; } if(sk_header[0] != 's' || sk_header[1] != 'k') { regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing" " SK record at offset 0x%.8X.", offset); goto fail_locked; } ret_val = talloc(NULL, REGFI_SK); if(ret_val == NULL) goto fail_locked; ret_val->offset = offset; /* XXX: Is there a way to be more conservative (shorter) with * cell length when cell is unallocated? */ ret_val->cell_size = cell_length; 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 & 0x00000007) != 0)) { regfi_log_add(REGFI_LOG_WARN, "Invalid cell size found while" " parsing SK record at offset 0x%.8X.", offset); goto fail_locked; } ret_val->magic[0] = sk_header[0]; ret_val->magic[1] = sk_header[1]; ret_val->unknown_tag = SVAL(sk_header, 0x2); ret_val->prev_sk_off = IVAL(sk_header, 0x4); ret_val->next_sk_off = IVAL(sk_header, 0x8); ret_val->ref_count = IVAL(sk_header, 0xC); ret_val->desc_size = IVAL(sk_header, 0x10); if((ret_val->prev_sk_off & 0x00000007) != 0 || (ret_val->next_sk_off & 0x00000007) != 0) { regfi_log_add(REGFI_LOG_WARN, "SK record's next/previous offsets" " are not a multiple of 8 while parsing SK record at" " offset 0x%.8X.", offset); goto fail_locked; } if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size) { regfi_log_add(REGFI_LOG_WARN, "Security descriptor too large for" " cell while parsing SK record at offset 0x%.8X.", offset); goto fail_locked; } sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size); if(sec_desc_buf == NULL) goto fail_locked; length = ret_val->desc_size; if(regfi_read(file->cb, sec_desc_buf, &length) != 0 || length != ret_val->desc_size) { regfi_log_add(REGFI_LOG_ERROR, "Failed to read security" " descriptor while parsing SK record at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_sk")) goto fail; if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, ret_val->desc_size))) { regfi_log_add(REGFI_LOG_ERROR, "Failed to parse security" " descriptor while parsing SK record at offset 0x%.8X.", offset); goto fail; } free(sec_desc_buf); return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_sk"); fail: if(sec_desc_buf != NULL) free(sec_desc_buf); talloc_free(ret_val); return NULL; } REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, uint32_t num_values, bool strict) { REGFI_VALUE_LIST* ret_val = NULL; uint32_t i, cell_length, length, read_len; bool unalloc; if(!regfi_lock(file, &file->cb_lock, "regfi_parse_valuelist")) goto fail; if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_ERROR, "Failed to read cell header" " while parsing value list at offset 0x%.8X.", offset); goto fail_locked; } if((cell_length & 0x00000007) != 0) { regfi_log_add(REGFI_LOG_WARN, "Cell length not a multiple of 8" " while parsing value list at offset 0x%.8X.", offset); if(strict) goto fail_locked; cell_length = cell_length & 0xFFFFFFF8; } if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t)) { regfi_log_add(REGFI_LOG_WARN, "Too many values found" " while parsing value list at offset 0x%.8X.", offset); if(strict) goto fail_locked; num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t); } read_len = num_values*sizeof(uint32_t); ret_val = talloc(NULL, REGFI_VALUE_LIST); if(ret_val == NULL) goto fail_locked; ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len); if(ret_val->elements == NULL) goto fail_locked; ret_val->offset = offset; ret_val->cell_size = cell_length; ret_val->num_values = num_values; length = read_len; if((regfi_read(file->cb, (uint8_t*)ret_val->elements, &length) != 0) || length != read_len) { regfi_log_add(REGFI_LOG_ERROR, "Failed to read value pointers" " while parsing value list at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist")) goto fail; for(i=0; i < num_values; i++) { /* Fix endianness */ ret_val->elements[i] = IVAL(&ret_val->elements[i], 0); /* Validate the first num_values values to ensure they make sense */ if(strict) { /* 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] & 0x00000007) != 0)) { regfi_log_add(REGFI_LOG_WARN, "Invalid value pointer" " (0x%.8X) found while parsing value list at offset" " 0x%.8X.", ret_val->elements[i], offset); goto fail; } } } return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist"); fail: talloc_free(ret_val); return NULL; } /* XXX: should give this boolean return type to indicate errors */ void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, 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. */ REGFI_BUFFER tmp_buf; REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME) ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE; if(vk->name_length == 0) return; if(from_encoding == file->string_encoding) { vk->name_raw[vk->name_length] = '\0'; vk->name = (char*)vk->name_raw; } else { tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding), regfi_encoding_int2str(file->string_encoding), vk->name_raw, vk->name_length); if(tmp_buf.buf == NULL) { regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting" " value name to encoding %s. VK offset: 0x%.8X." " Error message: %s", regfi_encoding_int2str(file->string_encoding), vk->offset, strerror(errno)); vk->name = NULL; } else { vk->name = (char*)tmp_buf.buf; talloc_reparent(NULL, vk, vk->name); } } } /****************************************************************************** ******************************************************************************/ REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, bool strict) { REGFI_VK* ret_val = NULL; int32_t max_size; max_size = regfi_calc_maxsize(file, offset); if(max_size < 0) return NULL; ret_val = regfi_parse_vk(file, offset, max_size, strict); if(ret_val == NULL) return NULL; regfi_interpret_valuename(file, ret_val, strict); return ret_val; } /****************************************************************************** * If !strict, the list may contain NULLs, VK records may point to NULL. ******************************************************************************/ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, uint32_t num_values, uint32_t max_size, bool strict) { uint32_t usable_num_values; if((num_values+1) * sizeof(uint32_t) > max_size) { regfi_log_add(REGFI_LOG_WARN, "Number of values indicated by" " parent key (%d) would cause cell to straddle HBIN" " boundary while loading value list at offset" " 0x%.8X.", num_values, offset); if(strict) return NULL; usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t); } else usable_num_values = num_values; return regfi_parse_valuelist(file, offset, usable_num_values, strict); } /* XXX: should give this boolean return type to indicate errors */ void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, 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. */ REGFI_BUFFER tmp_buf; REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE; if(nk->name_length == 0) return; if(from_encoding == file->string_encoding) { nk->name_raw[nk->name_length] = '\0'; nk->name = (char*)nk->name_raw; } else { tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding), regfi_encoding_int2str(file->string_encoding), nk->name_raw, nk->name_length); if(tmp_buf.buf == NULL) { regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting" " key name to encoding %s. NK offset: 0x%.8X." " Error message: %s", regfi_encoding_int2str(file->string_encoding), nk->offset, strerror(errno)); nk->name = NULL; } else { nk->name = (char*)tmp_buf.buf; talloc_reparent(NULL, nk, nk->name); } } } /****************************************************************************** * ******************************************************************************/ REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, bool strict) { REGFI_NK* nk; uint32_t off; int32_t max_size; if(file->nk_cache != NULL) { /* First, check to see if we have this key in our cache */ if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk")) return NULL; regfi_lock(file, &file->nk_lock, "regfi_load_nk"); nk = (REGFI_NK*)lru_cache_find(file->nk_cache, &offset, 4); if(nk != NULL) nk = talloc_reference(NULL, nk); regfi_unlock(file, &file->nk_lock, "regfi_load_nk"); regfi_unlock(file, &file->mem_lock, "regfi_load_nk"); if(nk != NULL) return nk; } /* Not cached currently, proceed with loading it */ max_size = regfi_calc_maxsize(file, offset); if (max_size < 0) return NULL; /* get the initial nk record */ if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Could not load NK record at" " offset 0x%.8X.", offset); return NULL; } regfi_interpret_keyname(file, nk, strict); /* get value list */ if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) { off = nk->values_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(file, off); if(max_size < 0) { if(strict) { talloc_free(nk); return NULL; } else nk->values = NULL; } else { nk->values = regfi_load_valuelist(file, off, nk->num_values, max_size, true); if(nk->values == NULL) { regfi_log_add(REGFI_LOG_WARN, "Could not load value list" " for NK record at offset 0x%.8X.", offset); if(strict) { talloc_free(nk); return NULL; } } talloc_reparent(NULL, nk, nk->values); } } /* now get subkey list */ if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) { off = nk->subkeys_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(file, off); if(max_size < 0) { if(strict) { talloc_free(nk); return NULL; } else nk->subkeys = NULL; } else { nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys, max_size, true); if(nk->subkeys == NULL) { regfi_log_add(REGFI_LOG_WARN, "Could not load subkey list" " while parsing NK record at offset 0x%.8X.", offset); nk->num_subkeys = 0; } talloc_reparent(NULL, nk, nk->subkeys); } } if(file->nk_cache != NULL) { /* All is well, so let us cache this key for later */ if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk")) return NULL; regfi_lock(file, &file->nk_lock, "regfi_load_nk"); lru_cache_update(file->nk_cache, &offset, 4, nk); regfi_unlock(file, &file->nk_lock, "regfi_load_nk"); regfi_unlock(file, &file->mem_lock, "regfi_load_nk"); } return nk; } /****************************************************************************** ******************************************************************************/ const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict) { REGFI_SK* ret_val = NULL; int32_t max_size; uint32_t* failure_ptr = NULL; max_size = regfi_calc_maxsize(file, offset); if(max_size < 0) return NULL; if(file->sk_cache == NULL) return regfi_parse_sk(file, offset, max_size, strict); if(!regfi_lock(file, &file->mem_lock, "regfi_load_sk")) return NULL; regfi_lock(file, &file->sk_lock, "regfi_load_sk"); /* First look if we have already parsed it */ ret_val = (REGFI_SK*)lru_cache_find(file->sk_cache, &offset, 4); /* Bail out if we have previously cached a parse failure at this offset. */ if(ret_val && *(uint32_t*)ret_val == REGFI_OFFSET_NONE) { ret_val = NULL; goto unlock; } if(ret_val == NULL) { 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); if(failure_ptr == NULL) goto unlock; *failure_ptr = REGFI_OFFSET_NONE; lru_cache_update(file->sk_cache, &offset, 4, failure_ptr); /* Let the cache be the only owner of this */ talloc_unlink(NULL, failure_ptr); } else lru_cache_update(file->sk_cache, &offset, 4, ret_val); } else ret_val = talloc_reference(NULL, ret_val); unlock: regfi_unlock(file, &file->sk_lock, "regfi_load_sk"); regfi_unlock(file, &file->mem_lock, "regfi_load_sk"); return ret_val; } /****************************************************************************** ******************************************************************************/ REGFI_NK* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin) { REGFI_NK* nk = NULL; 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; while(cur_offset < hbin_end) { if(!regfi_lock(file, &file->cb_lock, "regfi_find_root_nk")) return NULL; if(!regfi_parse_cell(file->cb, cur_offset, NULL, 0, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell at offset" " 0x%.8X while searching for root key.", cur_offset); goto error_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk")) return NULL; if(cell_length == 0) break; if(!unalloc) { nk = regfi_load_key(file, cur_offset, true); if(nk != NULL) { if(nk->flags & REGFI_NK_FLAG_ROOT) return nk; } } cur_offset += cell_length; } return NULL; error_locked: regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk"); return NULL; } /****************************************************************************** ******************************************************************************/ REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding) { REGFI_FILE* ret_val; REGFI_RAW_FILE* file_cb = talloc(NULL, REGFI_RAW_FILE); if(file_cb == NULL) return NULL; file_cb->state = (void*)talloc(file_cb, int); if(file_cb->state == NULL) goto fail; *(int*)file_cb->state = fd; file_cb->cur_off = 0; file_cb->size = 0; file_cb->read = ®fi_raw_read; file_cb->seek = ®fi_raw_seek; ret_val = regfi_alloc_cb(file_cb, output_encoding); if(ret_val == NULL) goto fail; /* In this case, we want file_cb to be freed when ret_val is */ talloc_reparent(NULL, ret_val, file_cb); return ret_val; fail: talloc_free(file_cb); return NULL; } /****************************************************************************** ******************************************************************************/ static int regfi_free_cb(void* f) { REGFI_FILE* file = (REGFI_FILE*)f; pthread_mutex_destroy(&file->cb_lock); pthread_rwlock_destroy(&file->hbins_lock); pthread_mutex_destroy(&file->sk_lock); pthread_mutex_destroy(&file->nk_lock); pthread_mutex_destroy(&file->mem_lock); return 0; } /****************************************************************************** ******************************************************************************/ REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb, REGFI_ENCODING output_encoding) { REGFI_FILE* rb; REGFI_HBIN* hbin = NULL; uint32_t hbin_off, cache_secret; int64_t file_length; bool rla; /* Determine file length. Must be at least big enough for the header * and one hbin. */ file_length = regfi_seek(file_cb, 0, SEEK_END); if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC) { regfi_log_add(REGFI_LOG_ERROR, "File length (%d) too short to contain a" " header and at least one HBIN.", file_length); return NULL; } regfi_seek(file_cb, 0, SEEK_SET); if(output_encoding != REGFI_ENCODING_UTF8 && output_encoding != REGFI_ENCODING_ASCII) { regfi_log_add(REGFI_LOG_ERROR, "Invalid output_encoding supplied" " in creation of regfi iterator."); return NULL; } /* Read file header */ if ((rb = regfi_parse_regf(file_cb, false)) == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Failed to read REGF block."); return NULL; } rb->file_length = file_length; rb->cb = file_cb; rb->string_encoding = output_encoding; if(pthread_mutex_init(&rb->cb_lock, NULL) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Failed to create cb_lock mutex."); goto fail; } if(pthread_rwlock_init(&rb->hbins_lock, NULL) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Failed to create hbins_lock rwlock."); goto fail; } if(pthread_mutex_init(&rb->sk_lock, NULL) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Failed to create sk_lock mutex."); goto fail; } if(pthread_mutex_init(&rb->nk_lock, NULL) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Failed to create nk_lock mutex."); goto fail; } if(pthread_mutex_init(&rb->mem_lock, NULL) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Failed to create mem_lock mutex."); goto fail; } rb->hbins = range_list_new(); if(rb->hbins == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Failed to create HBIN range_list."); goto fail; } talloc_reparent(NULL, rb, rb->hbins); rla = true; hbin_off = REGFI_REGF_SIZE; hbin = regfi_parse_hbin(rb, hbin_off, true); while(hbin && rla) { rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin); if(rla) talloc_reparent(NULL, rb->hbins, hbin); hbin_off = hbin->file_off + hbin->block_size; hbin = regfi_parse_hbin(rb, hbin_off, true); } /* This secret isn't very secret, but we don't need a good one. This * secret is just designed to prevent someone from trying to blow our * caching and make things slow. */ cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16); rb->sk_cache = NULL; if(REGFI_CACHE_SK_MAX > 0) rb->sk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_SK_MAX, cache_secret, true); rb->nk_cache = NULL; if(REGFI_CACHE_NK_MAX > 0) rb->nk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_NK_MAX, cache_secret, true); /* success */ talloc_set_destructor(rb, regfi_free_cb); return rb; fail: pthread_mutex_destroy(&rb->cb_lock); pthread_rwlock_destroy(&rb->hbins_lock); pthread_mutex_destroy(&rb->sk_lock); pthread_mutex_destroy(&rb->nk_lock); pthread_mutex_destroy(&rb->mem_lock); range_list_free(rb->hbins); talloc_free(rb); return NULL; } /****************************************************************************** ******************************************************************************/ void regfi_free(REGFI_FILE* file) { /* Callback handles cleanup side effects */ talloc_free(file); } /****************************************************************************** * First checks the offset given by the file header, then checks the * rest of the file if that fails. ******************************************************************************/ const REGFI_NK* regfi_get_rootkey(REGFI_FILE* file) { REGFI_NK* nk = NULL; REGFI_HBIN* hbin; uint32_t root_offset, i, num_hbins; if(!file) return NULL; root_offset = file->root_cell+REGFI_REGF_SIZE; nk = regfi_load_key(file, root_offset, true); if(nk != NULL) { if(nk->flags & REGFI_NK_FLAG_ROOT) return nk; } regfi_log_add(REGFI_LOG_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. */ if(!regfi_read_lock(file, &file->hbins_lock, "regfi_get_rootkey")) return NULL; num_hbins = range_list_size(file->hbins); for(i=0; i < num_hbins && nk == NULL; i++) { hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data; nk = regfi_find_root_nk(file, hbin); } if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_get_rootkey")) return NULL; return nk; } /****************************************************************************** *****************************************************************************/ void regfi_free_record(REGFI_FILE* file, const void* record) { if(!regfi_lock(file, &file->mem_lock, "regfi_free_record")) return; talloc_unlink(NULL, (void*)record); regfi_unlock(file, &file->mem_lock, "regfi_free_record"); } /****************************************************************************** *****************************************************************************/ const void* regfi_reference_record(REGFI_FILE* file, const void* record) { const void* ret_val = NULL; if(!regfi_lock(file, &file->mem_lock, "regfi_reference_record")) return ret_val; ret_val = talloc_reference(NULL, record); regfi_unlock(file, &file->mem_lock, "regfi_reference_record"); return ret_val; } /****************************************************************************** *****************************************************************************/ uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key) { uint32_t num_in_list = 0; if(key == NULL) return 0; if(key->subkeys != NULL) num_in_list = key->subkeys->num_keys; if(num_in_list != key->num_subkeys) { regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d keys in its" " subkey list but reports %d should be available.", key->offset, num_in_list, key->num_subkeys); return (num_in_list < key->num_subkeys)?num_in_list:key->num_subkeys; } return num_in_list; } /****************************************************************************** *****************************************************************************/ uint32_t regfi_fetch_num_values(const REGFI_NK* key) { uint32_t num_in_list = 0; if(key == NULL) return 0; if(key->values != NULL) num_in_list = key->values->num_values; if(num_in_list != key->num_values) { regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d values in" " its value list but reports %d should be available.", key->offset, num_in_list, key->num_values); return (num_in_list < key->num_values)?num_in_list:key->num_values; } return num_in_list; } /****************************************************************************** *****************************************************************************/ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file) { REGFI_NK* root; REGFI_ITERATOR* ret_val; ret_val = talloc(NULL, REGFI_ITERATOR); if(ret_val == NULL) return NULL; ret_val->cur = talloc(ret_val, REGFI_ITER_POSITION); if(ret_val->cur == NULL) { talloc_free(ret_val); return NULL; } ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH); if(ret_val->key_positions == NULL) { talloc_free(ret_val); return NULL; } talloc_reparent(NULL, ret_val, ret_val->key_positions); root = (REGFI_NK*)regfi_get_rootkey(file); if(root == NULL) { talloc_free(ret_val); return NULL; } ret_val->cur->offset = root->offset; if(root->subkeys_off == REGFI_OFFSET_NONE) ret_val->cur->num_subkeys = 0; else ret_val->cur->num_subkeys = regfi_fetch_num_subkeys(root); if(root->values_off == REGFI_OFFSET_NONE) ret_val->cur->num_values = 0; else ret_val->cur->num_values = regfi_fetch_num_values(root); ret_val->cur->cur_subkey = 0; ret_val->cur->cur_value = 0; ret_val->f = file; regfi_free_record(ret_val->f, root); return ret_val; } /****************************************************************************** *****************************************************************************/ void regfi_iterator_free(REGFI_ITERATOR* i) { talloc_unlink(NULL, i); } /****************************************************************************** *****************************************************************************/ /* XXX: some way of indicating reason for failure should be added. */ bool regfi_iterator_down(REGFI_ITERATOR* i) { REGFI_NK* subkey; REGFI_ITER_POSITION* pos = talloc(i, REGFI_ITER_POSITION); if(pos == NULL) return false; subkey = (REGFI_NK*)regfi_iterator_cur_subkey(i); if(subkey == NULL) { talloc_free(pos); return false; } if(!void_stack_push(i->key_positions, i->cur)) { talloc_free(pos); regfi_free_record(i->f, subkey); return false; } pos->offset = subkey->offset; if(subkey->subkeys_off == REGFI_OFFSET_NONE) pos->num_subkeys = 0; else pos->num_subkeys = regfi_fetch_num_subkeys(subkey); if(subkey->values_off == REGFI_OFFSET_NONE) pos->num_values = 0; else pos->num_values = regfi_fetch_num_values(subkey); pos->cur_subkey = 0; pos->cur_value = 0; i->cur = pos; regfi_free_record(i->f, subkey); return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_up(REGFI_ITERATOR* i) { REGFI_ITER_POSITION* pos; pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions); if(pos == NULL) return false; if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_up")) return false; talloc_unlink(i, i->cur); regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_up"); i->cur = pos; return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_to_root(REGFI_ITERATOR* i) { while(regfi_iterator_up(i)) continue; return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name) { const REGFI_NK* cur_key; uint32_t new_index; bool ret_val = false; cur_key = regfi_iterator_cur_key(i); if(cur_key == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_subkey."); return ret_val; } if(regfi_find_subkey(i->f, cur_key, name, &new_index)) { i->cur->cur_subkey = new_index; ret_val = true; } regfi_free_record(i->f, cur_key); return ret_val; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path) { uint32_t x; if(path == NULL) return false; for(x=0; ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x]) && regfi_iterator_down(i)); x++) { continue; } if(path[x] == NULL) { return true; } /* XXX: is this the right number of times? */ for(; x > 0; x--) regfi_iterator_up(i); return false; } /****************************************************************************** *****************************************************************************/ const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i) { const REGFI_NK* ret_val = NULL; ret_val = regfi_load_key(i->f, i->cur->offset, true); return ret_val; } /****************************************************************************** *****************************************************************************/ const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key) { if(key == NULL || key->sk_off == REGFI_OFFSET_NONE) return NULL; /*lru_cache_print(file->sk_cache);*/ return regfi_load_sk(file, key->sk_off + REGFI_REGF_SIZE, true); } /****************************************************************************** *****************************************************************************/ const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk) { if(sk == NULL || sk->next_sk_off == REGFI_OFFSET_NONE) return NULL; return regfi_load_sk(file, sk->next_sk_off + REGFI_REGF_SIZE, true); } /****************************************************************************** *****************************************************************************/ const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk) { if(sk == NULL || sk->prev_sk_off == REGFI_OFFSET_NONE) return NULL; return regfi_load_sk(file, sk->prev_sk_off + REGFI_REGF_SIZE, true); } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_first_subkey(REGFI_ITERATOR* i) { i->cur->cur_subkey = 0; return (i->cur->cur_subkey < i->cur->num_subkeys); } /****************************************************************************** *****************************************************************************/ const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i) { const REGFI_NK* cur_key; const REGFI_NK* ret_val; cur_key = regfi_iterator_cur_key(i); if(cur_key == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_subkey."); return NULL; } ret_val = regfi_get_subkey(i->f, cur_key, i->cur->cur_subkey); regfi_free_record(i->f, cur_key); return ret_val; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_next_subkey(REGFI_ITERATOR* i) { i->cur->cur_subkey++; return (i->cur->cur_subkey < i->cur->num_subkeys); } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name) { const REGFI_NK* cur_key; uint32_t new_index; bool ret_val = false; cur_key = regfi_iterator_cur_key(i); if(cur_key == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_value."); return ret_val; } if(regfi_find_value(i->f, cur_key, name, &new_index)) { i->cur->cur_value = new_index; ret_val = true; } regfi_free_record(i->f, cur_key); return ret_val; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_first_value(REGFI_ITERATOR* i) { i->cur->cur_value = 0; return (i->cur->cur_value < i->cur->num_values); } /****************************************************************************** *****************************************************************************/ const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i) { const REGFI_NK* cur_key; const REGFI_VK* ret_val = NULL; cur_key = regfi_iterator_cur_key(i); if(cur_key == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_value."); return ret_val; } ret_val = regfi_get_value(i->f, cur_key, i->cur->cur_value); regfi_free_record(i->f, cur_key); return ret_val; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_next_value(REGFI_ITERATOR* i) { i->cur->cur_value++; return (i->cur->cur_value < i->cur->num_values); } /****************************************************************************** *****************************************************************************/ const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i) { REGFI_NK** ret_val; void_stack_iterator* iter; const REGFI_ITER_POSITION* cur; uint16_t k, num_keys; num_keys = void_stack_size(i->key_positions)+1; ret_val = talloc_array(NULL, REGFI_NK*, num_keys+1); if(ret_val == NULL) return NULL; iter = void_stack_iterator_new(i->key_positions); if (iter == NULL) { talloc_free(ret_val); return NULL; } k=0; for(cur=void_stack_iterator_next(iter); cur != NULL; cur=void_stack_iterator_next(iter)) { ret_val[k++] = regfi_load_key(i->f, cur->offset, true); } ret_val[k] = regfi_load_key(i->f, i->cur->offset, true); void_stack_iterator_free(iter); if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry")) { talloc_free(ret_val); return NULL; } for(k=0; kf, &i->f->mem_lock, "regfi_iterator_ancestry"); ret_val[k] = NULL; return (const REGFI_NK**)ret_val; } /****************************************************************************** *****************************************************************************/ const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file, const REGFI_NK* key) { REGFI_CLASSNAME* ret_val; uint8_t* raw; REGFI_BUFFER tmp_buf; uint32_t offset; int32_t 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(file, offset); if(max_size <= 0) return NULL; parse_length = key->classname_length; raw = regfi_parse_classname(file, offset, &parse_length, max_size, true); if(raw == NULL) { regfi_log_add(REGFI_LOG_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->offset = offset; ret_val->raw = raw; ret_val->size = parse_length; talloc_reparent(NULL, ret_val, raw); tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE), regfi_encoding_int2str(file->string_encoding), raw, parse_length); if(tmp_buf.buf == NULL) { regfi_log_add(REGFI_LOG_WARN, "Error occurred while" " converting classname to charset %s. Error message: %s", file->string_encoding, strerror(errno)); ret_val->interpreted = NULL; } else { ret_val->interpreted = (char*)tmp_buf.buf; talloc_reparent(NULL, ret_val, tmp_buf.buf); } return ret_val; } /****************************************************************************** *****************************************************************************/ const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file, const REGFI_VK* value) { REGFI_DATA* ret_val = NULL; REGFI_BUFFER raw_data; if(value->data_size != 0) { raw_data = regfi_load_data(file, value->data_off, value->data_size, value->data_in_offset, true); if(raw_data.buf == NULL) { regfi_log_add(REGFI_LOG_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_log_add(REGFI_LOG_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(file, value->type, ret_val)) { regfi_log_add(REGFI_LOG_INFO, "Error occurred while" " interpreting data for VK record at offset 0x%.8X.", value->offset); } } } return ret_val; } /****************************************************************************** *****************************************************************************/ bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, const char* name, uint32_t* index) { const REGFI_NK* cur; uint32_t i; uint32_t num_subkeys = regfi_fetch_num_subkeys(key); bool found = false; /* XXX: should we allow "(default)" subkey names? * Do realistically they exist? */ if(name == NULL) return false; /* XXX: Should lazily build a hash table in memory to index where keys are when * there are a large number of subkeys. Attach this to cached keys to * bound the extra amount of memory used. */ for(i=0; (i < num_subkeys) && (found == false); i++) { cur = regfi_get_subkey(file, key, i); if(cur == NULL) return false; /* A NULL name signifies the "(default)" value for a key */ if(cur->name != NULL && (strcasecmp(cur->name, name) == 0)) { found = true; *index = i; } regfi_free_record(file, cur); } return found; } /****************************************************************************** *****************************************************************************/ bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key, const char* name, uint32_t* index) { const REGFI_VK* cur; uint32_t i; uint32_t num_values = regfi_fetch_num_values(key); bool found = false; /* XXX: Should lazily build a hash table in memory to index where values are when * there are a large number of them. Attach this to cached keys to * bound the extra amount of memory used. */ for(i=0; (i < num_values) && (found == false); i++) { cur = regfi_get_value(file, key, i); if(cur == NULL) return false; /* A NULL name signifies the "(default)" value for a key */ if(((name == NULL) && (cur->name == NULL)) || ((name != NULL) && (cur->name != NULL) && (strcasecmp(cur->name, name) == 0))) { found = true; *index = i; } regfi_free_record(file, cur); } return found; } /****************************************************************************** *****************************************************************************/ const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, uint32_t index) { if(index < regfi_fetch_num_subkeys(key)) { return regfi_load_key(file, key->subkeys->elements[index].offset+REGFI_REGF_SIZE, true); } return NULL; } /****************************************************************************** *****************************************************************************/ const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, uint32_t index) { if(index < regfi_fetch_num_values(key)) { return regfi_load_value(file, key->values->elements[index]+REGFI_REGF_SIZE, true); } return NULL; } /****************************************************************************** *****************************************************************************/ const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key) { if(key != NULL && key->parent_off != REGFI_OFFSET_NONE) return regfi_load_key(file, key->parent_off+REGFI_REGF_SIZE, true); return NULL; } /****************************************************************************** *****************************************************************************/ 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_reparent(NULL, 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, uint32_t type, REGFI_DATA* data) { REGFI_BUFFER tmp_buf; uint8_t** tmp_array; uint32_t i, j; 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_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE), regfi_encoding_int2str(file->string_encoding), data->raw, data->size); if(tmp_buf.buf == NULL) { regfi_log_add(REGFI_LOG_INFO, "Error occurred while" " converting data of type %d to string encoding %d." " Error message: %s", type, file->string_encoding, strerror(errno)); data->interpreted.string = NULL; data->interpreted_size = 0; return false; } data->interpreted.string = tmp_buf.buf; data->interpreted_size = tmp_buf.len; talloc_reparent(NULL, data, tmp_buf.buf); 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: /* Attempt to convert entire string from UTF-16LE to output encoding, * then parse and quote fields individually. */ tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE), regfi_encoding_int2str(file->string_encoding), data->raw, data->size); if(tmp_buf.buf == NULL) { regfi_log_add(REGFI_LOG_INFO, "Error occurred while" " converting data of type %d to string encoding %d." " Error message: %s", type, file->string_encoding, strerror(errno)); data->interpreted.multiple_string = NULL; data->interpreted_size = 0; return false; } tmp_array = talloc_array(NULL, uint8_t*, tmp_buf.len+1); if(tmp_array == NULL) { talloc_free(tmp_buf.buf); data->interpreted.string = NULL; data->interpreted_size = 0; return false; } tmp_array[0] = tmp_buf.buf; for(i=0,j=1; i < tmp_buf.len && j < tmp_buf.len; i++) { if(tmp_buf.buf[i] == '\0' && (i+1 < tmp_buf.len) && tmp_buf.buf[i+1] != '\0') tmp_array[j++] = tmp_buf.buf+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_buf.len; talloc_reparent(NULL, tmp_array, tmp_buf.buf); talloc_reparent(NULL, 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 string from input_charset to output_charset. * On error, returns a NULL buf attribute and sets the errno. *****************************************************************************/ REGFI_BUFFER regfi_conv_charset(const char* input_charset, const char* output_charset, uint8_t* input, uint32_t input_len) { iconv_t conv_desc; char* inbuf = (char*)input; char* outbuf; char* retbuf; size_t allocated = (size_t)input_len; size_t in_left = (size_t)input_len; size_t out_left = (size_t)allocated-1; REGFI_BUFFER ret_val; int ret; ret_val.buf = NULL; ret_val.len = 0; retbuf = talloc_array(NULL, char, allocated); outbuf = retbuf; if(outbuf == NULL) { errno = ENOMEM; return ret_val; } /* Set up conversion descriptor. */ /* XXX: Consider creating a couple of conversion descriptors earlier, * storing them on an iterator so they don't have to be recreated * each time. */ conv_desc = iconv_open(output_charset, input_charset); ret = 0; do { if(ret == -1) { retbuf = talloc_realloc(NULL, retbuf, char, allocated+(in_left*2)); if(retbuf == NULL) { errno = ENOMEM; return ret_val; } outbuf = retbuf+(allocated-1-out_left); out_left += in_left*2; allocated += in_left*2; } ret = iconv(conv_desc, &inbuf, &in_left, &outbuf, &out_left); } while(ret == -1 && errno == E2BIG); if(ret == -1) { iconv_close(conv_desc); return ret_val; } /* Save memory */ if(out_left > 0) { retbuf = talloc_realloc(NULL, retbuf, char, allocated-out_left); if(retbuf == NULL) { errno = ENOMEM; return ret_val; } allocated -= out_left; } retbuf[allocated-1] = '\0'; iconv_close(conv_desc); ret_val.buf = (uint8_t*)retbuf; ret_val.len = allocated-1; return ret_val; } /******************************************************************* * Computes the checksum of the registry file header. * buffer must be at least the size of a regf header (4096 bytes). *******************************************************************/ static uint32_t regfi_compute_header_checksum(uint8_t* buffer) { uint32_t checksum, x; int i; /* XOR of all bytes 0x0000 - 0x01FB */ checksum = x = 0; for ( i=0; i<0x01FB; i+=4 ) { x = IVAL(buffer, i ); checksum ^= x; } return checksum; } /******************************************************************* *******************************************************************/ REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict) { uint8_t file_header[REGFI_REGF_SIZE]; uint32_t length; REGFI_FILE* ret_val; ret_val = talloc(NULL, REGFI_FILE); if(ret_val == NULL) return NULL; ret_val->sk_cache = NULL; ret_val->hbins = NULL; length = REGFI_REGF_SIZE; if((regfi_read(file_cb, file_header, &length)) != 0 || length != REGFI_REGF_SIZE) { regfi_log_add(REGFI_LOG_WARN, "Read failed while parsing REGF structure."); goto fail; } ret_val->checksum = IVAL(file_header, 0x1FC); ret_val->computed_checksum = regfi_compute_header_checksum(file_header); if (strict && (ret_val->checksum != ret_val->computed_checksum)) { regfi_log_add(REGFI_LOG_WARN, "Stored header checksum (%.8X) did not equal" " computed checksum (%.8X).", ret_val->checksum, ret_val->computed_checksum); if(strict) goto fail; } memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE); if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Magic number mismatch " "(%.2X %.2X %.2X %.2X) while parsing hive header", ret_val->magic[0], ret_val->magic[1], ret_val->magic[2], ret_val->magic[3]); goto fail; } ret_val->sequence1 = IVAL(file_header, 0x4); ret_val->sequence2 = IVAL(file_header, 0x8); ret_val->mtime = ((uint64_t)IVAL(file_header, 0x10)) << 32; ret_val->mtime |= IVAL(file_header, 0xC); ret_val->major_version = IVAL(file_header, 0x14); ret_val->minor_version = IVAL(file_header, 0x18); ret_val->type = IVAL(file_header, 0x1C); ret_val->format = IVAL(file_header, 0x20); ret_val->root_cell = IVAL(file_header, 0x24); ret_val->last_block = IVAL(file_header, 0x28); ret_val->cluster = IVAL(file_header, 0x2C); memcpy(ret_val->file_name, file_header+0x30, REGFI_REGF_NAME_SIZE); ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16); if(ret_val->rm_id == NULL) regfi_log_add(REGFI_LOG_WARN, "Hive header's rm_id failed to parse."); ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16); if(ret_val->log_id == NULL) regfi_log_add(REGFI_LOG_WARN, "Hive header's log_id failed to parse."); ret_val->flags = IVAL(file_header, 0x90); ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16); if(ret_val->tm_id == NULL) regfi_log_add(REGFI_LOG_WARN, "Hive header's tm_id failed to parse."); ret_val->guid_signature = IVAL(file_header, 0xa4); memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE); memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE); ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16); ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16); ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16); ret_val->boot_type = IVAL(file_header, 0xFF8); ret_val->boot_recover = IVAL(file_header, 0xFFC); return ret_val; fail: talloc_free(ret_val); return NULL; } /****************************************************************************** * 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_t offset, bool strict) { REGFI_HBIN* hbin = NULL; uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE]; uint32_t length; if(offset >= file->file_length) goto fail; if(!regfi_lock(file, &file->cb_lock, "regfi_parse_hbin")) goto fail; if(regfi_seek(file->cb, offset, SEEK_SET) == -1) { regfi_log_add(REGFI_LOG_ERROR, "Seek failed" " while parsing hbin at offset 0x%.8X.", offset); goto fail_locked; } length = REGFI_HBIN_HEADER_SIZE; if((regfi_read(file->cb, hbin_header, &length) != 0) || length != REGFI_HBIN_HEADER_SIZE) { regfi_log_add(REGFI_LOG_ERROR, "Read failed" " while parsing hbin at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin")) goto fail; hbin = talloc(NULL, REGFI_HBIN); if(hbin == NULL) goto fail; hbin->file_off = offset; memcpy(hbin->magic, hbin_header, 4); if(strict && (memcmp(hbin->magic, "hbin", 4) != 0)) { /* This always seems to happen at the end of a file, so we make it an INFO * message, rather than something more serious. */ regfi_log_add(REGFI_LOG_INFO, "Magic number mismatch " "(%.2X %.2X %.2X %.2X) while parsing hbin at offset" " 0x%.8X.", hbin->magic[0], hbin->magic[1], hbin->magic[2], hbin->magic[3], offset); goto fail; } hbin->first_hbin_off = IVAL(hbin_header, 0x4); hbin->block_size = IVAL(hbin_header, 0x8); /* this should be the same thing as hbin->block_size, but just in case */ hbin->next_block = IVAL(hbin_header, 0x1C); /* Ensure the block size is a multiple of 0x1000 and doesn't run off * the end of the file. */ /* XXX: This may need to be relaxed for dealing with * partial or corrupt files. */ if((offset + hbin->block_size > file->file_length) || (hbin->block_size & 0xFFFFF000) != hbin->block_size) { regfi_log_add(REGFI_LOG_ERROR, "The hbin offset is not aligned" " or runs off the end of the file" " while parsing hbin at offset 0x%.8X.", offset); goto fail; } return hbin; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin"); fail: talloc_free(hbin); return NULL; } /******************************************************************* *******************************************************************/ REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { uint8_t nk_header[REGFI_NK_MIN_LENGTH]; REGFI_NK* ret_val; uint32_t length,cell_length; bool unalloc = false; ret_val = talloc(NULL, REGFI_NK); if(ret_val == NULL) { regfi_log_add(REGFI_LOG_ERROR, "Failed to allocate memory while" " parsing NK record at offset 0x%.8X.", offset); goto fail; } if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk")) goto fail; if(!regfi_parse_cell(file->cb, offset, nk_header, REGFI_NK_MIN_LENGTH, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header" " while parsing NK record at offset 0x%.8X.", offset); goto fail_locked; } if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k')) { regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing" " NK record at offset 0x%.8X.", offset); goto fail_locked; } ret_val->values = NULL; ret_val->subkeys = NULL; ret_val->offset = offset; ret_val->cell_size = cell_length; 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 & 0x00000007) != 0)) { regfi_log_add(REGFI_LOG_WARN, "A length check failed while" " parsing NK record at offset 0x%.8X.", offset); goto fail_locked; } ret_val->magic[0] = nk_header[0x0]; ret_val->magic[1] = nk_header[0x1]; ret_val->flags = SVAL(nk_header, 0x2); if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0) { regfi_log_add(REGFI_LOG_WARN, "Unknown key flags (0x%.4X) while" " parsing NK record at offset 0x%.8X.", (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset); } ret_val->mtime = ((uint64_t)IVAL(nk_header, 0x8)) << 32; ret_val->mtime |= IVAL(nk_header, 0x4); /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990 * or later than Jan 1, 2290, we consider this a bad key. This helps * weed out some false positives during deleted data recovery. */ if(unalloc && (ret_val->mtime < REGFI_MTIME_MIN || ret_val->mtime > REGFI_MTIME_MAX)) { goto fail_locked; } ret_val->unknown1 = IVAL(nk_header, 0xC); ret_val->parent_off = IVAL(nk_header, 0x10); ret_val->num_subkeys = IVAL(nk_header, 0x14); ret_val->unknown2 = IVAL(nk_header, 0x18); ret_val->subkeys_off = IVAL(nk_header, 0x1C); ret_val->unknown3 = IVAL(nk_header, 0x20); ret_val->num_values = IVAL(nk_header, 0x24); ret_val->values_off = IVAL(nk_header, 0x28); ret_val->sk_off = IVAL(nk_header, 0x2C); ret_val->classname_off = IVAL(nk_header, 0x30); ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34); ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38); ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C); ret_val->max_bytes_value = IVAL(nk_header, 0x40); ret_val->unk_index = IVAL(nk_header, 0x44); ret_val->name_length = SVAL(nk_header, 0x48); ret_val->classname_length = SVAL(nk_header, 0x4A); ret_val->name = NULL; if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size) { if(strict) { regfi_log_add(REGFI_LOG_ERROR, "Contents too large for cell" " while parsing NK record at offset 0x%.8X.", offset); goto fail_locked; } else ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH; } else if (unalloc) { /* Truncate cell_size if it's much larger than the apparent total record length. */ /* Round up to the next multiple of 8 */ length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8; if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH) length+=8; /* If cell_size is still greater, truncate. */ if(length < ret_val->cell_size) ret_val->cell_size = length; } /* +1 to length in case we decided to use this directly as a string later */ ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1); if(ret_val->name_raw == NULL) goto fail_locked; /* Don't need to seek, should be at the right offset */ length = ret_val->name_length; if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0) || length != ret_val->name_length) { regfi_log_add(REGFI_LOG_ERROR, "Failed to read key name" " while parsing NK record at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk")) goto fail; return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"); fail: talloc_free(ret_val); return NULL; } uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, uint16_t* name_length, uint32_t max_size, bool strict) { uint8_t* ret_val = NULL; uint32_t length; uint32_t cell_length; bool unalloc = false; if(*name_length <= 0 || offset == REGFI_OFFSET_NONE || (offset & 0x00000007) != 0) { goto fail; } if(!regfi_lock(file, &file->cb_lock, "regfi_parse_classname")) goto fail; if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header" " while parsing class name at offset 0x%.8X.", offset); goto fail_locked; } if((cell_length & 0x0000007) != 0) { regfi_log_add(REGFI_LOG_ERROR, "Cell length not a multiple of 8" " while parsing class name at offset 0x%.8X.", offset); goto fail_locked; } if(cell_length > max_size) { regfi_log_add(REGFI_LOG_WARN, "Cell stretches past hbin " "boundary while parsing class name at offset 0x%.8X.", offset); if(strict) goto fail_locked; cell_length = max_size; } if((cell_length - 4) < *name_length) { regfi_log_add(REGFI_LOG_WARN, "Class name is larger than" " cell_length while parsing class name at offset" " 0x%.8X.", offset); if(strict) goto fail_locked; *name_length = cell_length - 4; } ret_val = talloc_array(NULL, uint8_t, *name_length); if(ret_val != NULL) { length = *name_length; if((regfi_read(file->cb, ret_val, &length) != 0) || length != *name_length) { regfi_log_add(REGFI_LOG_ERROR, "Could not read class name" " while parsing class name at offset 0x%.8X.", offset); goto fail_locked; } } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_classname")) goto fail; return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_classname"); fail: talloc_free(ret_val); return NULL; } /****************************************************************************** *******************************************************************************/ REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_VK* ret_val; uint8_t vk_header[REGFI_VK_MIN_LENGTH]; uint32_t raw_data_size, length, cell_length; bool unalloc = false; ret_val = talloc(NULL, REGFI_VK); if(ret_val == NULL) goto fail; if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk")) goto fail; if(!regfi_parse_cell(file->cb, offset, vk_header, REGFI_VK_MIN_LENGTH, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header" " while parsing VK record at offset 0x%.8X.", offset); goto fail_locked; } ret_val->offset = offset; ret_val->cell_size = cell_length; ret_val->name = NULL; ret_val->name_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 & 0x00000007) != 0) { regfi_log_add(REGFI_LOG_WARN, "Invalid cell size encountered" " while parsing VK record at offset 0x%.8X.", offset); goto fail_locked; } ret_val->magic[0] = vk_header[0x0]; ret_val->magic[1] = vk_header[0x1]; if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k')) { /* XXX: This does not account for deleted keys under Win2K which * often have this (and the name length) overwritten with * 0xFFFF. */ regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch" " while parsing VK record at offset 0x%.8X.", offset); goto fail_locked; } 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->flags = SVAL(vk_header, 0x10); ret_val->unknown1 = SVAL(vk_header, 0x12); if(ret_val->name_length > 0) { if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size) { regfi_log_add(REGFI_LOG_WARN, "Name too long for remaining cell" " space while parsing VK record at offset 0x%.8X.", offset); if(strict) goto fail_locked; else ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4; } /* Round up to the next multiple of 8 */ cell_length = (ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) & 0xFFFFFFF8; if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) cell_length+=8; /* +1 to length in case we decided to use this directly as a string later */ ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1); if(ret_val->name_raw == NULL) goto fail_locked; length = ret_val->name_length; if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0) || length != ret_val->name_length) { regfi_log_add(REGFI_LOG_ERROR, "Could not read value name" " while parsing VK record at offset 0x%.8X.", offset); goto fail_locked; } } else cell_length = REGFI_VK_MIN_LENGTH + 4; if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk")) goto fail; if(unalloc) { /* If cell_size is still greater, truncate. */ if(cell_length < ret_val->cell_size) ret_val->cell_size = cell_length; } return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_vk"); fail: talloc_free(ret_val); return NULL; } /****************************************************************************** * ******************************************************************************/ 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_t cell_length, offset; int32_t max_size; bool unalloc; /* 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_log_add(REGFI_LOG_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 { offset = voffset + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(file, offset); if(max_size < 0) { regfi_log_add(REGFI_LOG_WARN, "Could not find HBIN for data" " at offset 0x%.8X.", offset); goto fail; } if(!regfi_lock(file, &file->cb_lock, "regfi_load_data")) goto fail; if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while" " parsing data record at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_load_data")) goto fail; if((cell_length & 0x00000007) != 0) { regfi_log_add(REGFI_LOG_WARN, "Cell length not multiple of 8" " while parsing data record at offset 0x%.8X.", offset); goto fail; } if(cell_length > max_size) { regfi_log_add(REGFI_LOG_WARN, "Cell extends past HBIN boundary" " while parsing data record at offset 0x%.8X.", offset); goto fail; } if(cell_length - 4 < length) { /* 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? */ 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, NULL, strict); } else { regfi_log_add(REGFI_LOG_WARN, "Data length (0x%.8X) larger than" " remaining cell length (0x%.8X)" " while parsing data record at offset 0x%.8X.", length, cell_length - 4, offset); if(strict) goto fail; else length = cell_length - 4; } } ret_val = regfi_parse_data(file, offset, length, strict); } return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_load_data"); fail: ret_val.buf = NULL; ret_val.len = 0; return ret_val; } /****************************************************************************** * 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((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL) goto fail; ret_val.len = length; if(!regfi_lock(file, &file->cb_lock, "regfi_parse_data")) goto fail; if(regfi_seek(file->cb, offset+4, SEEK_SET) == -1) { regfi_log_add(REGFI_LOG_WARN, "Could not seek while " "reading data at offset 0x%.8X.", offset); goto fail_locked; } read_length = length; if((regfi_read(file->cb, ret_val.buf, &read_length) != 0) || read_length != length) { regfi_log_add(REGFI_LOG_ERROR, "Could not read data block while" " parsing data record at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_data")) goto fail; return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_data"); fail: 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_log_add(REGFI_LOG_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_parse_big_data_header(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_BUFFER ret_val; uint32_t cell_length; bool unalloc; /* 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_log_add(REGFI_LOG_WARN, "Big data header exceeded max_size " "while parsing big data header at offset 0x%.8X.",offset); goto fail; } if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_header")) goto fail; if(!regfi_parse_cell(file->cb, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while" " parsing big data header at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header")) goto fail; if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b')) { regfi_log_add(REGFI_LOG_WARN, "Unknown magic number" " (0x%.2X, 0x%.2X) encountered while parsing" " big data header at offset 0x%.8X.", ret_val.buf[0], ret_val.buf[1], offset); goto fail; } ret_val.len = REGFI_BIG_DATA_MIN_LENGTH; return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header"); fail: 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_lock(file, &file->cb_lock, "regfi_parse_big_data_indirect")) goto fail; if(!regfi_parse_cell(file->cb, offset, (uint8_t*)ret_val, num_chunks*sizeof(uint32_t), &indirect_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while" " parsing big data indirect record at offset 0x%.8X.", offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect")) goto fail; /* Convert pointers to proper endianess, verify they are aligned. */ for(i=0; icb_lock, "regfi_parse_big_data_indirect"); fail: 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; icb_lock, "regfi_parse_big_data_cells")) goto fail; chunk_offset = offsets[i]+REGFI_REGF_SIZE; if(!regfi_parse_cell(file->cb, chunk_offset, NULL, 0, &cell_length, &unalloc)) { regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while" " parsing big data chunk at offset 0x%.8X.", chunk_offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells")) goto fail; if(!range_list_add(ret_val, chunk_offset, cell_length, NULL)) goto fail; } return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells"); 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; (i0); 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(cell_info->length - 8 >= data_left) { if(i+1 != num_chunks) { regfi_log_add(REGFI_LOG_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 = cell_info->length - 8; if(read_length > regfi_calc_maxsize(file, cell_info->offset)) { regfi_log_add(REGFI_LOG_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(!regfi_lock(file, &file->cb_lock, "regfi_load_big_data")) goto fail; if(regfi_seek(file->cb, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1) { regfi_log_add(REGFI_LOG_WARN, "Could not seek to chunk while " "constructing big data at offset 0x%.8X " "(chunk offset 0x%.8X).", offset, cell_info->offset); goto fail_locked; } tmp_len = read_length; if(regfi_read(file->cb, ret_val.buf+(data_length-data_left), &read_length) != 0 || (read_length != tmp_len)) { regfi_log_add(REGFI_LOG_WARN, "Could not read data chunk while" " constructing big data at offset 0x%.8X" " (chunk offset 0x%.8X).", offset, cell_info->offset); goto fail_locked; } if(!regfi_unlock(file, &file->cb_lock, "regfi_load_big_data")) 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; } range_list_free(bd_cells); ret_val.len = data_length-data_left; return ret_val; fail_locked: regfi_unlock(file, &file->cb_lock, "regfi_load_big_data"); fail: talloc_free(ret_val.buf); talloc_free(indirect_ptrs); if(bd_cells != NULL) range_list_free(bd_cells); ret_val.buf = NULL; ret_val.len = 0; return ret_val; } range_list* regfi_parse_unalloc_cells(REGFI_FILE* file) { range_list* ret_val; REGFI_HBIN* hbin; const range_list_element* hbins_elem; uint32_t i, num_hbins, curr_off, cell_len; bool is_unalloc; ret_val = range_list_new(); if(ret_val == NULL) return NULL; if(!regfi_read_lock(file, &file->hbins_lock, "regfi_parse_unalloc_cells")) { range_list_free(ret_val); return NULL; } num_hbins = range_list_size(file->hbins); for(i=0; ihbins, i); if(hbins_elem == NULL) break; hbin = (REGFI_HBIN*)hbins_elem->data; curr_off = REGFI_HBIN_HEADER_SIZE; while(curr_off < hbin->block_size) { if(!regfi_lock(file, &file->cb_lock, "regfi_parse_unalloc_cells")) break; if(!regfi_parse_cell(file->cb, hbin->file_off+curr_off, NULL, 0, &cell_len, &is_unalloc)) { regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells"); break; } if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells")) break; if((cell_len == 0) || ((cell_len & 0x00000007) != 0)) { regfi_log_add(REGFI_LOG_ERROR, "Bad cell length encountered" " while parsing unallocated cells at offset 0x%.8X.", hbin->file_off+curr_off); break; } /* for some reason the record_size of the last record in an hbin block can extend past the end of the block even though the record fits within the remaining space....aaarrrgggghhhhhh */ if(curr_off + cell_len >= hbin->block_size) cell_len = hbin->block_size - curr_off; if(is_unalloc) range_list_add(ret_val, hbin->file_off+curr_off, cell_len, NULL); curr_off = curr_off+cell_len; } } if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_parse_unalloc_cells")) { range_list_free(ret_val); return NULL; } return ret_val; } /* From lib/time.c */ /**************************************************************************** Returns an 8 byte filetime from a time_t This takes real GMT as input and converts to kludge-GMT ****************************************************************************/ REGFI_NTTIME regfi_unix2nt_time(time_t t) { double d; if (t==0) return 0L; if (t == TIME_T_MAX) return 0x7fffffffffffffffL; if (t == -1) return 0xffffffffffffffffL; /* 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) + REGFI_TIME_FIXUP; d *= 1.0e7; /* nt->high = (uint32_t)(d * (1.0/c)); nt->low = (uint32_t)(d - ((double)nt->high) * c); */ return (REGFI_NTTIME) d; } /**************************************************************************** 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. ****************************************************************************/ double regfi_nt2unix_time(REGFI_NTTIME nt) { double ret_val; if (nt == 0 || nt == 0xffffffffffffffffL) return 0; ret_val = (double)(nt) * 1.0e-7; /* now adjust by 369 years to make the secs since 1970 */ ret_val -= REGFI_TIME_FIXUP; /* 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_val; } /* End of stuff from lib/time.c */ reglookup+git/doc/reglookup-timeline.1.docbook000664 001750 001750 00000007763 12566244653 022601 0ustar00sophiesophie000000 000000 reglookup-timeline 1 File Conversion Utilities reglookup-timeline Windows NT+ registry MTIME timeline generator SYNOPSIS reglookup-timeline [-H] registry-file [registry-file ...] DESCRIPTION This script is a wrapper for reglookup(1), and reads one or more registry files to produce an MTIME-sorted output. This is helpful when building timelines for forensic investigations. PARAMETERS reglookup-timeline accepts one or more registry file names. All of the provided registries will be parsed using reglookup(1). The -H option may be used to omit the header line. OUTPUT reglookup-timeline generates a comma-separated values (CSV) compatible format to stdout. While the output of reglookup-timeline and reglookup(1) differ in the columns returned, the base format is the same. Currently, reglookup-timeline returns three columns: MTIME, FILE, and PATH. Only rows representing registry keys are returned, since MTIMEs are not stored for values. The FILE column indicates which registry file (provided as an argument) the key came from. Finally, the PATH field contains the full registry path to the key. Records are returned sorted in ascending order based on the MTIME column. BUGS This script is new, and as such it's interface may change significantly over the next few revisions. In particular, additional command line options will likely be added, and the output of the script may be altered in minor ways. It is very difficult to find documentation on what precise operations cause the MTIMEs to be updated. Basic experimentation indicates that a key's stamp is updated anytime an immediate sub-value or sub-key is created, renamed, deleted, or it's value is modified. If this MTIME data is critical to an investigation, any conclusions should be validated through experimentation in a controlled lab environment. This software should be considered unstable at this time. CREDITS This script was written by Timothy D. Morgan based on suggestions from Uwe Danz. Please see source code for a full list of copyrights. LICENSE Please see the file "LICENSE" included with this software distribution. 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 version 3 for more details. SEE ALSO reglookup(1) reglookup-recover(1) reglookup+git/doc/reglookup-recover.1.docbook000664 001750 001750 00000014520 12566244653 022425 0ustar00sophiesophie000000 000000 reglookup 1 File Conversion Utilities reglookup-recover Windows NT+ registry deleted data recovery tool SYNOPSIS reglookup-recover [options] registry-file DESCRIPTION reglookup-recover attempts to scour a Windows registry hive for deleted data structures and outputs those found in a CSV-like format. OPTIONS reglookup-recover accepts the following parameters: Verbose output. Enables the printing of a column header row. (default) Disables the printing of a column header row. Display cells which could not be interpreted as valid registry structures at the end of the output. Do not display cells which could not be interpreted as valid registry structures. This is the default behavior. Display raw cell contents for cells which were interpreted as intact data structures. This additional output will appear on the same line as the interpreted data. Do not display raw cell contents for cells which were interpreted as intact data structures. This is the default behavior. Required argument. Specifies the location of the registry file to read. The system registry files should be found under: %SystemRoot%/system32/config. OUTPUT reglookup-recover generates a comma-separated values (CSV) like output and writes it to stdout. For more information on the syntax of the general format, see reglookup(1). This tool is new and the output format, particularly the included columns, may change in future revisions. When this format stablizes, additional documentation will be included here. EXAMPLES To dump the recoverable contents of a system registry hive: reglookup-recover /mnt/win/c/WINDOWS/system32/config/system Extract all available unallocated data, including unparsable unallocated space and the raw data associated with parsed cells in a user-specific registry: reglookup-recover -r -l '/mnt/win/c/Documents and Settings/user/NTUSER.DAT' BUGS This program has been smoke-tested against most current Windows target platforms, but a comprehensive test suite has not yet been developed. (Please report results to the development mailing list if you encounter any bugs. Sample registry files and/or patches are greatly appreciated.) This program is new as of RegLookup release 0.9.0 and should be considered unstable. For more information on registry format details and the recovery algorithm, see: http://sentinelchicken.com/research/registry_format/ http://sentinelchicken.com/research/registry_recovery/ CREDITS This program was written by Timothy D. Morgan. LICENSE Please see the file "LICENSE" included with this software distribution. 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 version 3 for more details. SEE ALSO reglookup-timeline(1) reglookup-recover(1) reglookup+git/lib/Makefile000664 001750 001750 00000001260 12566244653 016710 0ustar00sophiesophie000000 000000 # $Id: Makefile 30 2005-07-16 14:31:27Z tim $ ################################################################################ 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 winsec.o: winsec.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c void_stack.o: void_stack.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ void_stack.c range_list.o: range_list.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ range_list.c lru_cache.o: lru_cache.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ lru_cache.c talloc.o: talloc.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ talloc.c clean: rm -f $(FILES) reglookup+git/python/experimental/SConstruct000664 001750 001750 00000003075 12566244653 022560 0ustar00sophiesophie000000 000000 import os, os.path, pdb from functools import partial import utils import config import class_parser def build_python_bindings(target, source, env, initialization=''): """ A command to generate python bindings """ module_name = os.path.splitext(os.path.basename(target[0].name))[0] utils.warn("Generating automatic python bindings for module %s" % module_name) p = class_parser.HeaderParser(module_name, verbose=config.V) p.module.init_string = initialization p.parse_filenames([s.get_abspath() for s in source]) fd = open(target[0].get_abspath(), 'w') p.write(fd) fd.close() nenv = utils.ExtendedEnvironment() BOUND_FILES = Split(""" ../include/regfi.h regfi/pyregfi.h """) nenv.config = config if config.DEBUG: nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC -ggdb -O0") nenv.Append(CPPPATH=['../include', 'include']) nenv.python_cppflags = '-I/usr/include/python2.5_d' else: nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC") nenv.Append(CPPPATH=['../include', 'include']) nenv.Append(LIBPATH="../lib") nenv.Append(LINKFLAGS="") # XXX: why should I need to call regfi_init() when it should be called only once automatically? nenv.Command('regfi/pyregfi.c', BOUND_FILES, partial(build_python_bindings, initialization='pyregfi_init();regfi_init();')) nenv.Depends('regfi/pyregfi.c', 'class_parser.py') nenv.PythonModule("pyregfi", ['regfi/pyregfi.c', 'regfi/regfi.c', 'regfi/class.c', 'regfi/error.c'], LIBS=['regfi', 'talloc']) reglookup+git/bin/000775 001750 001750 00000000000 12566244653 015253 5ustar00sophiesophie000000 000000 reglookup+git/python/experimental/inspect.py000664 001750 001750 00000020134 12566244653 022540 0ustar00sophiesophie000000 000000 #!/usr/bin/python """ Module to inspect the contents of an AFF4 volume """ import sys, pdb, os import re, cmd, readline import optparse import pyaff4 import shlex import fnmatch, time import subprocess import readline time.sleep(1) ## Set more sane completer delimiters readline.set_completer_delims(' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?') ## Configure colors colors = {} colors['cyan'] = '\033[96m' colors['purple'] = '\033[95m' colors['blue'] = '\033[94m' colors['green'] = '\033[92m' colors['yellow'] = '\033[93m' colors['red'] = '\033[91m' colors['end'] = '\033[0m' #If the output is not a terminal, remove the colors if not sys.stdout.isatty(): for key, value in colors.iteritems(): colors[key] = '' parser = optparse.OptionParser() (options, args) = parser.parse_args() ## Load the specified args as volumes oracle = pyaff4.Resolver() VOLUMES = [] STREAMS = {} for arg in args: volume = pyaff4.RDFURN() volume.set(arg) if oracle.load(volume): VOLUMES.append(volume) urn = pyaff4.RDFURN() iter = oracle.get_iter(volume, pyaff4.AFF4_CONTAINS) while oracle.iter_next(iter, urn): print urn.value if not urn: break ## We store is as a simplified path path = "/%s%s" % (urn.parser.netloc, urn.parser.query) STREAMS[path] = urn class Inspector(cmd.Cmd): prompt = "/:>" intro = "Inspecting the volumes %s. Type 'help' for help." % args CWD = "/" def cmdloop(self, *args): while 1: try: if not cmd.Cmd.cmdloop(self, *args): break except KeyboardInterrupt,e: print "Type %sexit%s to exit, %shelp%s for help" % ( colors['yellow'], colors['end'], colors['cyan'], colors['end']) def do_EOF(self, args): """ Exit this program """ print "%sGoodbye%s" % (colors['yellow'], colors['end']) return True def do_cd(self, line): """ Change directory """ args = shlex.split(line) if not args[0].startswith("/"): args[0] = self.CWD + args[0] try: potential_cd = os.path.normpath(args[0]) if not potential_cd.endswith('/'): potential_cd += "/" for s in STREAMS: if s.startswith(potential_cd): self.CWD = potential_cd self.prompt = "%s:>" % self.CWD return raise IOError("No such directory %s" % potential_cd) except IndexError: print "CWD: %s" % self.CWD def complete_cd(self, *args): return self.complete_stream(*args) def do_help(self, line): """ Provide help for commands """ args = shlex.split(line) if not args: ## List all commands for k in dir(self): if k.startswith("do_"): method = getattr(self, k) print "%s%s%s: %s" % (colors['red'], k[3:], colors['end'], method.__doc__) return for arg in args: try: method = getattr(self, "do_%s" % arg) except AttributeError: print "%sError:%s Command %s%s%s not found" % (colors['red'], colors['end'], colors['yellow'], arg, colors['end']) print "%s: %s" % (arg, method.__doc__) def do_ls(self, line): """ List streams matching a glob expression """ globs = shlex.split(line) if not globs: globs=[self.CWD] result = [] for s in STREAMS: for g in globs: if fnmatch.fnmatch(s, g + "*"): if s.startswith(self.CWD): s = s[len(self.CWD):] path = s.split("/")[0] if path == s: decoration = colors['blue'] else: decoration = colors['end'] if path not in result: print "%s%s%s" % (decoration, path, colors['end']) result.append(path) def do_less(self, line): """ Read the streams specified and pipe them through the pager """ globs = shlex.split(line) for s in STREAMS: for g in globs: if not g.startswith("/"): g = self.CWD + g g = os.path.normpath(g) if fnmatch.fnmatch(s, g): ## Try to open the stream try: fd = oracle.open(STREAMS[s], 'r') except IOError, e: raise pager = os.environ.get("PAGER","less") pipe=os.popen(pager,"w") while 1: data = fd.read(1024 * 1024) if not data: break pipe.write(data) pipe.close() def do_cp(self, line): """ Copy a stream from a source to a destination. """ globs = shlex.split(line) src = globs[0] dest = globs[1] if(len(globs) > 2): print "usage: cp src dest" return if not src.startswith("/"): src = self.CWD + src src = os.path.normpath(src) src_urn = pyaff4.RDFURN() src_urn.set("aff4:/" + src) ## Try to open the stream try: fd = oracle.open(src_urn, 'r') except IOError, e: raise dest_fd = open(dest, "w") while 1: data = fd.read(1024 * 1024) if not data: break dest_fd.write(data) dest_fd.close() def complete_cp(self, *args): return self.complete_stream(*args) def complete_less(self, *args): return self.complete_stream(*args) def _display_attribute(self, iter): while 1: obj = oracle.alloc_from_iter(iter) if not obj: break print " -> type (%s) " % (obj.dataType) print " -> data (%s) " % (obj.serialise(iter.urn)) def do_resolve(self, line): globs = shlex.split(line) attribute = pyaff4.XSDString() subject = pyaff4.RDFURN() iter = pyaff4.RESOLVER_ITER() subject.set(globs[0]) try: attribute.set(globs[1]) print attribute.value self._display_attribute(iter) except IndexError: ## Just display all the attributes while oracle.attributes_iter(subject, attribute, iter): print attribute.value self._display_attribute(iter) def complete_stream(self, text, line, begidx, endidx): if not text.startswith("/"): text = self.CWD + text if not text: completions = STREAMS.keys() completions = [ text + f[len(text):].split("/")[0] for f in STREAMS.keys() if f.startswith(text) ] return completions def do_pwd(self, line): print "CWD: %s" % self.CWD def do_exit(self, *args): """ Exits the program """ return self.do_EOF(*args) def emptyline(self): return def onecmd(self, line): try: return cmd.Cmd.onecmd(self, line) except Exception,e: print "%sError:%s %s%s%s %s" % (colors['red'], colors['end'], colors['yellow'], e.__class__.__name__, colors['end'], e) return None Inspector().cmdloop() reglookup+git/doc/devel/Doxyfile.pyregfi000664 001750 001750 00000177773 12566244653 021547 0ustar00sophiesophie000000 000000 # Doxyfile 1.6.2-20100208 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = pyregfi # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc/devel/pyregfi # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = YES # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set # FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = YES # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort # the (brief and detailed) documentation of class members so that constructors # and destructors are listed first. If set to NO (the default) the # constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief # docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if # SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = python/pyregfi # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.py # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = talloc* # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = . # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = NO # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for # more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted # before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML # help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client using # Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is # that it scales better to large projects and allows full text search. The # disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code # with syntax highlighting in the LaTeX output. Note that which sources are # shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = doc/devel/regfi/regfi.tags=../regfi # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES reglookup+git/SConstruct000664 001750 001750 00000011247 12566244653 016542 0ustar00sophiesophie000000 000000 #-*- mode: Python;-*- import sys import os sys.dont_write_bytecode = True from regfi_version import REGFI_VERSION ABI_VERSION=REGFI_VERSION.rsplit('.',1)[0] # Package Maintainers: should any of these options in the first line be omitted during # package build, instead relying on CFLAGS/LDFLAGS to specify them when appropriate? cflags = '-std=gnu99 -pedantic -Wall -D_FILE_OFFSET_BITS=64 -fvisibility=hidden' cflags += ' -DREGFI_VERSION=\'"%s"\' ' % REGFI_VERSION cflags += os.environ.get('CFLAGS','-fPIE -pie -fstack-protector -D_FORTIFY_SOURCE=2') linkflags = "-fPIC " + os.environ.get('LDFLAGS',"-Wl,-z,relro,-z,now") lib_src = ['lib/regfi.c', 'lib/winsec.c', 'lib/range_list.c', 'lib/lru_cache.c', 'lib/void_stack.c'] cc=os.environ.get('CC', 'gcc') env = Environment(ENV=os.environ, CC=cc, CFLAGS=cflags, LINKFLAGS=linkflags, CPPPATH=['include', '/usr/local/include'], LIBPATH=['lib', '/usr/local/lib'], LIBS=['m', 'pthread', 'regfi', 'talloc']) # Libraries libregfi_static = env.Library(lib_src) libregfi = env.SharedLibrary(lib_src, LIBS=['m','pthread', 'talloc'], SHLIBVERSION=ABI_VERSION) # Executables reglookup = env.Program(['src/reglookup.c']) reglookup_recover = env.Program(['src/reglookup-recover.c']) # Documentation # This only needs to be run during the release/packaging process man_fixup = "|sed 's/.SH DESCRIPTION/\\n.SH DESCRIPTION/'" man_builder = Builder(action='docbook2x-man --to-stdout $SOURCE' + man_fixup + '| gzip -9 > $TARGET', suffix = '.gz', src_suffix = '.docbook') env['BUILDERS']['ManPage'] = man_builder man_reglookup = env.ManPage('doc/reglookup.1.docbook') man_reglookup_recover = env.ManPage('doc/reglookup-recover.1.docbook') man_reglookup_timeline = env.ManPage('doc/reglookup-timeline.1.docbook') # Installation prefix = os.environ.get('PREFIX','/usr/local')+'/' destdir = os.environ.get('DESTDIR','') bindir = os.environ.get('BINDIR', prefix + 'bin') libdir = os.environ.get('LIBDIR', prefix + 'lib') includedir = os.environ.get('INCLUDEDIR', prefix + 'include') mandir = os.environ.get('MANDIR', prefix + 'man') install_bin = [destdir + bindir, destdir + mandir] install_lib = [destdir + libdir, destdir + includedir + '/regfi'] env.Install(destdir+bindir, [reglookup, reglookup_recover, 'bin/reglookup-timeline']) libinstall = env.InstallVersionedLib(destdir+libdir, [libregfi, libregfi_static], SHLIBVERSION=ABI_VERSION) env.Install(destdir+includedir+'/regfi', Glob('include/*.h')) env.Install(destdir+mandir+'/man1', [man_reglookup, man_reglookup_recover, man_reglookup_timeline]) if os.getuid() == 0 and destdir == '': env.AddPostAction(libinstall, 'ldconfig') install_pyregfi = [] if sys.version_info[0] == 2: install_pyregfi.append('pyregfi2-install.log') env.Command('pyregfi2-install.log', ['python/pyregfi/__init__.py', 'python/pyregfi/structures.py', 'python/pyregfi/winsec.py'], "python setup.py install --root=/%s | tee pyregfi2-install.log" % destdir) python_path = os.popen('which python3').read() if python_path != '': install_pyregfi.append('pyregfi3-install.log') env.Command('pyregfi3-install.log', ['python/pyregfi/__init__.py', 'python/pyregfi/structures.py', 'python/pyregfi/winsec.py'], "python3 setup.py install --root=/%s | tee pyregfi3-install.log" % destdir) # API documentation regfi_doc = env.Command('doc/devel/regfi/index.html', Glob('lib/*.c')+Glob('include/*.h')+['doc/devel/Doxyfile.regfi'], 'doxygen doc/devel/Doxyfile.regfi') pyregfi_doc = env.Command('doc/devel/pyregfi/index.html', Glob('python/pyregfi/*.py')+['doc/devel/Doxyfile.pyregfi', regfi_doc], 'doxygen doc/devel/Doxyfile.pyregfi') install_items = install_bin + install_lib + install_pyregfi # User Friendly Targets env.Alias('libregfi', libregfi) env.Alias('reglookup', reglookup) env.Alias('reglookup-recover', reglookup_recover) env.Alias('bin', [reglookup_recover, reglookup]) env.Alias('doc', [man_reglookup,man_reglookup_recover,man_reglookup_timeline]) env.Alias('doc-devel', [regfi_doc, pyregfi_doc]) env.Alias('install_bin', install_bin) env.Alias('install_lib', install_lib) env.Alias('install_pyregfi', install_pyregfi) env.Alias('install', install_items) Default('bin', libregfi) reglookup+git/python/experimental/include/aff4_errors.h000664 001750 001750 00000003752 12566244653 024540 0ustar00sophiesophie000000 000000 /* ** aff4_errors.h ** ** Made by mic ** Login ** ** Started on Sat Mar 6 20:54:25 2010 mic ** Last update Sat Mar 6 20:54:25 2010 mic */ #ifndef AFF4_ERRORS_H_ # define AFF4_ERRORS_H_ // Some helpful little things #define ERROR_BUFFER_SIZE 1024 /** This is used for error reporting. This is similar to the way python does it, i.e. we set the error flag and return NULL. */ enum _error_type { EZero,EGeneric,EOverflow,EWarning, EUnderflow,EIOError, ENoMemory, EInvalidParameter, ERuntimeError, EKeyError, // Reserved for impossible conditions EProgrammingError }; void *raise_errors(enum _error_type t, char *string, ...); /** We only set the error state if its not already set */ #define RaiseError(t, message, ...) \ if(*aff4_get_current_error(NULL) == EZero) { \ raise_errors(t, "%s: (%s:%d) " message, __FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__); \ }; #define LogWarnings(format, ...) \ do { \ RaiseError(EWarning, format, ## __VA_ARGS__); \ PrintError(); \ } while(0); #define ClearError() \ do {*aff4_get_current_error(NULL) = EZero;} while(0); #define PrintError() \ do {char *error_str; if(*aff4_get_current_error(&error_str)) fprintf(stdout, "%s", error_str); fflush(stdout); ClearError(); }while(0); #define CheckError(error) \ (*aff4_get_current_error(NULL) == error) /** The current error state is returned by this function. This is done in a thread safe manner. */ enum _error_type *aff4_get_current_error(char **error_str); // These macros are used when we need to do something which might // change the error state on the error path of a function. #define PUSH_ERROR_STATE { enum _error_type *tmp_error_p = aff4_get_current_error(NULL); enum _error_type tmp_error = *tmp_error_p; enum _error_type exception __attribute__((unused)); #define POP_ERROR_STATE *tmp_error_p = tmp_error;}; void error_init(); #endif /* !AFF4_ERRORS_H_ */ reglookup+git/doc/devel/Doxyfile.regfi000664 001750 001750 00000177760 12566244653 021172 0ustar00sophiesophie000000 000000 # Doxyfile 1.6.2-20100208 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = regfi # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc/devel/regfi # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. Note that for custom extensions you also need to set # FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = YES # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort # the (brief and detailed) documentation of class members so that constructors # and destructors are listed first. If set to NO (the default) the # constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief # docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if # SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = lib include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = talloc* # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = . # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = NO # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for # more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted # before the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using HTML # help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client using # Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server based approach is # that it scales better to large projects and allows full text search. The # disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include source code # with syntax highlighting in the LaTeX output. Note that which sources are # shown also depends on other settings such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = doc/devel/regfi/regfi.tags # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES reglookup+git/python/experimental/regfi/class.c000664 001750 001750 00000003665 12566244653 023100 0ustar00sophiesophie000000 000000 /****************************************************** # Copyright 2004: Commonwealth of Australia. # # Developed by the Computer Network Vulnerability Team, # Information Security Group. # Department of Defence. # # Michael Cohen # # ****************************************************** # Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$ # ****************************************************** # # * This program is free software; you can redistribute it and/or # * modify it under the terms of the GNU General Public License # * as published by the Free Software Foundation; either version 2 # * of the License, or (at your option) any later version. # * # * This program is distributed in the hope that it will be useful, # * but WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # * GNU General Public License for more details. # * # * You should have received a copy of the GNU General Public License # * along with this program; if not, write to the Free Software # * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ******************************************************/ #include "class.h" #define BUFF_SIZE 1024 // Noone should instantiate Object directly. this should be already // allocated therefore: void Object_init(Object this) { this->__class__ = &__Object; this->__super__ = NULL; } struct Object_t __Object = { .__class__ = &__Object, .__super__ = &__Object, .__size = sizeof(struct Object_t), .__name__ = "Object" }; int issubclass(Object obj, Object class) { obj = obj->__class__; while(1) { if(obj == class->__class__) return 1; obj=obj->__super__; if(obj == &__Object || obj==NULL) return 0; }; } void unimplemented(Object self) { printf("%s contains unimplemented functions.. is it an abstract class?\n", NAMEOF(self)); abort(); } reglookup+git/include/range_list.h000664 001750 001750 00000012722 12566244653 020432 0ustar00sophiesophie000000 000000 /* * 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 * 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$ */ /** * @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 #define _RANGE_LIST_H #include #include #include #include #include #include #include "compat.h" typedef struct _range_list_element { uint32_t offset; uint32_t length; void* data; } range_list_element; /** XXX: document this. */ typedef struct _range_list { range_list_element** elements; uint32_t elem_alloced; uint32_t size; } range_list; /** Allocates a new range_list. * * @return A newly allocated range_list, or NULL if an error occurred. */ _EXPORT() range_list* range_list_new(); /** 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. * * @param rl the range_list to be free()d. */ _EXPORT() void range_list_free(range_list* rl); /** Query the current number of elements on a range_list * * @param rl the range_list to query * * @return The number of elements currently in the list. */ _EXPORT() uint32_t range_list_size(const range_list* rl); /** 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. */ _EXPORT() bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data); /** Removes an element from the list. * * The element data structure will be freed, but the data property will not be. * * @param rl the range_list to modify * @param index the element index to remove * * @return true if the element was successfully removed, false otherwise. */ _EXPORT() bool range_list_remove(range_list* rl, uint32_t index); /** Retrieves the element for a given index. * * @param rl the range_list being queried. * @param index the element index desired. * * @return The element for a given index, or NULL if the element is not * available. */ _EXPORT() const range_list_element* range_list_get(const range_list* rl, uint32_t index); /** Attempts to find the unique element whose range encompasses offset. * * @param rl the range_list being queried. * @param offset the location for which an element is desired. * * @return A matching element index or a negative value if none could be found. */ _EXPORT() int32_t range_list_find(const range_list* rl, uint32_t offset); /** Same as range_list_find(), but returns the data associated with an element. * * @param rl the range_list being queried. * @param offset the address to search for in the ranges * * @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 the data * element was never set. */ _EXPORT() void* range_list_find_data(const range_list* rl, uint32_t offset); /** 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. * * Both the original element and the newly created element will reference the * original element's data. * * @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. */ _EXPORT() bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset); /** Determines whether or not a specified range exists contiguously within the * range_list. * * @param rl the range_list to search * @param start the offset at the beginning of the range * @param length the length of the range * * @return true if the specified range exists and is complete, false otherwise. */ _EXPORT() bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length); #endif reglookup+git/python/experimental/include/000775 001750 001750 00000000000 12566244653 022144 5ustar00sophiesophie000000 000000 reglookup+git/setup.py000664 001750 001750 00000000446 12566244653 016221 0ustar00sophiesophie000000 000000 # Called from scons with appropriate python version import sys from distutils.core import setup sys.dont_write_bytecode = True from regfi_version import REGFI_VERSION sys.dont_write_bytecode = False setup(name='pyregfi', version=REGFI_VERSION, package_dir={'':'python'}, packages=['pyregfi']) reglookup+git/src/reglookup-recover.c000664 001750 001750 00000062602 12566244653 021116 0ustar00sophiesophie000000 000000 /* * This program attempts to recover deleted data structures in a registry hive. * * 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 * 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$ */ #include #include #include "talloc.h" #include "regfi.h" #include "range_list.h" #include "lru_cache.h" /* Globals, influenced by command line parameters */ bool print_verbose = false; bool print_security = false; bool print_header = true; bool print_leftover = false; bool print_parsedraw = false; const char* registry_file = NULL; #include "common.c" char* getQuotedData(REGFI_RAW_FILE* file_cb, uint32_t offset, uint32_t length) { uint8_t* buf; char* quoted_buf; uint32_t len; if((regfi_seek(file_cb, offset, SEEK_SET)) == -1) return NULL; buf = (uint8_t*)malloc(length); if(buf == NULL) return NULL; len = length; if((regfi_read(file_cb, buf, &length) != 0) || length != len) { free(buf); return NULL; } quoted_buf = quote_buffer(buf, length, common_special_chars); free(buf); return quoted_buf; } /* XXX: Somewhere in here, need to start looking for and handling classnames */ void printKey(REGFI_FILE* f, REGFI_NK* nk, const char* prefix) { char mtime[24]; char* quoted_name = NULL; char* quoted_raw = ""; formatTime(nk->mtime, mtime); /* XXX: Add command line option to choose output encoding */ regfi_interpret_keyname(f, nk, true); quoted_name = get_quoted_keyname(nk); if (quoted_name == NULL) { quoted_name = malloc(1*sizeof(char)); if(quoted_name == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); quoted_name[0] = '\0'; fprintf(stderr, "WARN: NULL key name in NK record at offset %.8X.\n", nk->offset); } if(print_parsedraw) quoted_raw = getQuotedData(f->cb, nk->offset, nk->cell_size); printf("%.8X,%.8X,KEY,%s,%s,%s,%d,,,,,,,,%s\n", nk->offset, nk->cell_size, prefix, quoted_name, mtime, nk->num_values, quoted_raw); if(print_parsedraw) free(quoted_raw); free(quoted_name); } void printValue(REGFI_FILE* f, REGFI_VK* vk, const char* prefix) { char* quoted_value = NULL; char* quoted_name = NULL; char* quoted_raw = ""; char* conv_error = NULL; const char* str_type = NULL; /* XXX: Add command line option to choose output encoding */ regfi_interpret_valuename(f, vk, true); 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 * ambiguity with a literal "(default)" value. The data type of a line * in the output allows one to differentiate between the parent key and * this value. */ quoted_name = strdup(""); if(quoted_name == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); } /* XXX: Add command line option to choose output encoding */ if(vk->data != NULL && !regfi_interpret_data(f, 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)); if(quoted_value == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); quoted_value[0] = '\0'; if(conv_error == NULL) fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Memory allocation failure likely.\n", prefix, quoted_name); else if(print_verbose) fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Returned error: %s\n", prefix, quoted_name, conv_error); } /* XXX: should these always be printed? */ else if(conv_error != NULL && print_verbose) fprintf(stderr, "INFO: While quoting value for '%s/%s', " "warning returned: %s\n", prefix, quoted_name, conv_error); if(print_parsedraw) quoted_raw = getQuotedData(f->cb, vk->offset, vk->cell_size); str_type = regfi_type_val2str(vk->type); if(str_type == NULL) printf("%.8X,%.8X,VALUE,%s,%s,,,0x%.8X,%s,%d,,,,,%s\n", vk->offset, vk->cell_size, prefix, quoted_name, vk->type, quoted_value, vk->data_size, quoted_raw); else printf("%.8X,%.8X,VALUE,%s,%s,,,%s,%s,%d,,,,,%s\n", vk->offset, vk->cell_size, prefix, quoted_name, str_type, quoted_value, vk->data_size, quoted_raw); if(print_parsedraw) free(quoted_raw); if(quoted_value != NULL) free(quoted_value); if(quoted_name != NULL) free(quoted_name); if(conv_error != NULL) free(conv_error); } void printSK(REGFI_FILE* f, REGFI_SK* sk) { char* quoted_raw = NULL; char* empty_str = ""; char* owner = regfi_get_owner(sk->sec_desc); char* group = regfi_get_group(sk->sec_desc); char* sacl = regfi_get_sacl(sk->sec_desc); char* dacl = regfi_get_dacl(sk->sec_desc); if(print_parsedraw) quoted_raw = getQuotedData(f->cb, sk->offset, sk->cell_size); if(owner == NULL) owner = empty_str; if(group == NULL) group = empty_str; if(sacl == NULL) sacl = empty_str; if(dacl == NULL) dacl = empty_str; printf("%.8X,%.8X,SK,,,,,,,,%s,%s,%s,%s,%s\n", sk->offset, sk->cell_size, owner, group, sacl, dacl, quoted_raw); if(owner != empty_str) free(owner); if(group != empty_str) free(group); if(sacl != empty_str) free(sacl); if(dacl != empty_str) free(dacl); if(print_parsedraw) free(quoted_raw); } int printCell(REGFI_FILE* f, uint32_t offset) { char* quoted_buf; uint32_t cell_length; bool unalloc; if(!regfi_parse_cell(f->cb, offset, NULL, 0, &cell_length, &unalloc)) return 1; quoted_buf = getQuotedData(f->cb, offset, cell_length); if(quoted_buf == NULL) return 2; printf("%.8X,%.8X,RAW,,,,,,,,,,,,%s\n", offset, cell_length, quoted_buf); free(quoted_buf); return 0; } /* This function returns a properly quoted parent path or partial parent * path for a given key. Returns NULL on error, "" if no path was available. * Paths returned must be free()d. */ /* XXX: This is not terribly efficient, as it may reparse many keys * repeatedly. Should try to add caching. */ char* getParentPath(REGFI_FILE* f, REGFI_NK* nk) { void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH); REGFI_NK* cur_ancestor; char* ret_val; 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) { offset = virt_offset+REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(f, offset); if(max_size < 0) virt_offset = REGFI_OFFSET_NONE; else { 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->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) { /* XXX: Add command line option to choose output encoding */ regfi_interpret_keyname(f, cur_ancestor, 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)) { /* XXX: Need to add a warning here */ regfi_free_record(f, cur_ancestor); void_stack_free(path_stack); return NULL; } /* Path element and preceeding delimiter * Note that this integer can't overflow since key name lengths are * 16 bits and the max depth is 512. */ path_element->len = strlen((char*)path_element->buf); ret_val_size += path_element->len + 1; regfi_free_record(f, cur_ancestor); } } } stack_size = void_stack_size(path_stack); ret_val_used = 0; ret_val = malloc(ret_val_size); if(ret_val == NULL) { void_stack_free(path_stack); return NULL; } ret_val[0] = '\0'; for(i=0; ibuf); ret_val_used += path_element->len + 1; free(path_element->buf); talloc_free(path_element); } void_stack_free(path_stack); return ret_val; } static void usage(void) { fprintf(stderr, "Usage: reglookup-recover [options] \n"); fprintf(stderr, "Version: %s\n", regfi_version()); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t-v\t sets verbose mode.\n"); fprintf(stderr, "\t-h\t enables header row. (default)\n"); fprintf(stderr, "\t-H\t disables header row.\n"); fprintf(stderr, "\t-l\t enables leftover(raw) cell output.\n"); fprintf(stderr, "\t-L\t disables leftover(raw) cell output. (default)\n"); fprintf(stderr, "\t-r\t enables raw cell output for parsed cells.\n"); fprintf(stderr, "\t-R\t disables raw cell output for parsed cells. (default)\n"); fprintf(stderr, "\n"); } bool removeRange(range_list* rl, uint32_t offset, uint32_t length) { int32_t rm_idx; const range_list_element* cur_elem; rm_idx = range_list_find(rl, offset); if(rm_idx < 0) { fprintf(stderr, "DEBUG: removeRange: rm_idx < 0; (%d)\n", rm_idx); return false; } cur_elem = range_list_get(rl, rm_idx); if(cur_elem == NULL) { fprintf(stderr, "DEBUG: removeRange: cur_elem == NULL. rm_idx=%d\n", rm_idx); return false; } if(offset > cur_elem->offset) { if(!range_list_split_element(rl, rm_idx, offset)) { fprintf(stderr, "DEBUG: removeRange: first split failed\n"); return false; } rm_idx++; cur_elem = range_list_get(rl, rm_idx); if(cur_elem == NULL) { fprintf(stderr, "DEBUG: removeRange: cur_elem == NULL after first split. rm_idx=%d\n", rm_idx); return false; } } if(offset+length < cur_elem->offset+cur_elem->length) { if(!range_list_split_element(rl, rm_idx, offset+length)) { fprintf(stderr, "DEBUG: removeRange: second split failed\n"); return false; } } if(!range_list_remove(rl, rm_idx)) { fprintf(stderr, "DEBUG: removeRange: remove failed\n"); return false; } return true; } int extractVKs(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_values) { const range_list_element* cur_elem; REGFI_VK* vk; uint32_t i, j; for(i=0; i < range_list_size(unalloc_cells); i++) { printMsgs(f); cur_elem = range_list_get(unalloc_cells, i); for(j=0; j <= cur_elem->length; j+=8) { vk = regfi_parse_vk(f, cur_elem->offset+j, cur_elem->length-j, false); printMsgs(f); if(vk != NULL) { if(!range_list_add(unalloc_values, vk->offset, vk->cell_size, vk)) { fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n"); return 20; } j+=vk->cell_size-8; } } } /* Remove value ranges from the unalloc_cells before we continue. */ for(i=0; ioffset, cur_elem->length)) return 30; } return 0; } int extractDataCells(REGFI_FILE* file, range_list* unalloc_cells, range_list* unalloc_values) { const range_list_element* cur_elem; REGFI_VK* vk; range_list* bd_cells; REGFI_BUFFER data; 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; idata; if(vk == NULL) return 11; length = vk->data_size; vk->data = NULL; if(vk->data_size != 0) { offset = vk->data_off+REGFI_REGF_SIZE; if(vk->data_in_offset) data = regfi_parse_little_data(file, vk->data_off, length, false); else { max_size = regfi_calc_maxsize(file, offset); if(max_size >= 0 && regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc) && (cell_length & 0x00000007) == 0 && cell_length <= max_size) { if(cell_length - 4 < length) { /* 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? */ 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; joffset, 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; joffset, 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; } } } /* 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_reparent(NULL, vk, vk->data); } } range_list_free(bd_cells); return 0; } /* NOTE: unalloc_keys should be an empty range_list. */ int extractKeys(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_keys) { const range_list_element* cur_elem; REGFI_NK* key = NULL; uint32_t i, j; int error_code = 0; for(i=0; i < range_list_size(unalloc_cells); i++) { printMsgs(f); cur_elem = range_list_get(unalloc_cells, i); for(j=0; cur_elem->length > REGFI_NK_MIN_LENGTH && j <= cur_elem->length-REGFI_NK_MIN_LENGTH; j+=8) { key = regfi_parse_nk(f, cur_elem->offset+j, cur_elem->length-j, false); printMsgs(f); if(key != NULL) { if(!range_list_add(unalloc_keys, key->offset, key->cell_size, key)) { fprintf(stderr, "ERROR: Couldn't add key to unalloc_keys.\n"); error_code = 20; goto fail; } talloc_reparent(NULL, unalloc_keys, key); j+=key->cell_size-8; } } } for(i=0; ioffset, cur_elem->length)) { error_code = 30; goto fail; } } return 0; fail: regfi_free_record(f, key); return error_code; } int extractValueLists(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_keys, range_list* unalloc_linked_values) { REGFI_NK* nk; REGFI_VK* vk; const range_list_element* cur_elem; uint32_t i, j, num_keys, off, values_length; int32_t max_size; num_keys=range_list_size(unalloc_keys); for(i=0; idata; if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) { off = nk->values_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(f, off); if(max_size >= 0) { nk->values = regfi_load_valuelist(f, off, nk->num_values, max_size, false); if(nk->values != NULL && nk->values->elements != NULL) { /* Number of elements in the value list may be shorter than advertised * by NK record due to cell truncation. We'll consider this valid and * only throw out the whole value list if it bleeds into an already * parsed structure. */ values_length = (nk->values->num_values+1)*sizeof(uint32_t); if(values_length != (values_length & 0xFFFFFFF8)) values_length = (values_length & 0xFFFFFFF8) + 8; if(!range_list_has_range(unalloc_cells, off, values_length)) { /* We've parsed a values-list which isn't in the unallocated list, * so prune it. */ talloc_free(nk->values); nk->values = NULL; } else { /* Values-list was recovered. Remove from unalloc_cells and * inspect values. */ if(!removeRange(unalloc_cells, off, values_length)) return 20; for(j=0; j < nk->values->num_values; j++) { /* Don't bother to restrict cell length here, since we'll * check our unalloc_cells range_list later. */ vk = regfi_parse_vk(f, nk->values->elements[j]+REGFI_REGF_SIZE, 0x7FFFFFFF, false); printMsgs(f); if(vk != NULL) { if(range_list_has_range(unalloc_cells, vk->offset, vk->cell_size)) { if(!range_list_add(unalloc_linked_values, vk->offset, vk->cell_size, vk)) { talloc_free(vk); return 30; } if(!removeRange(unalloc_cells, vk->offset, vk->cell_size)) return 40; } else talloc_free(vk); } } } } } } } return 0; } /* NOTE: unalloc_sks should be an empty range_list. */ int extractSKs(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_sks) { const range_list_element* cur_elem; REGFI_SK* sk; uint32_t i, j; for(i=0; i < range_list_size(unalloc_cells); i++) { printMsgs(f); cur_elem = range_list_get(unalloc_cells, i); for(j=0; j <= cur_elem->length; j+=8) { sk = regfi_parse_sk(f, cur_elem->offset+j, cur_elem->length-j, false); printMsgs(f); if(sk != NULL) { if(!range_list_add(unalloc_sks, sk->offset, sk->cell_size, sk)) { fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n"); return 20; } talloc_reparent(NULL, unalloc_sks, sk); j+=sk->cell_size-8; } } } for(i=0; ioffset, cur_elem->length)) return 30; } return 0; } int main(int argc, char** argv) { REGFI_FILE* f; const range_list_element* cur_elem; range_list* unalloc_cells; range_list* unalloc_keys; range_list* unalloc_linked_values; range_list* unalloc_values; range_list* unalloc_sks; char** parent_paths; char* tmp_name; char* tmp_path; REGFI_NK* tmp_key; REGFI_VK* tmp_value; uint32_t argi, arge, i, j, ret, num_unalloc_keys; int fd; /* Process command line arguments */ if(argc < 2) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n"); } arge = argc-1; for(argi = 1; argi < arge; argi++) { if (strcmp("-v", argv[argi]) == 0) print_verbose = true; else if (strcmp("-h", argv[argi]) == 0) print_header = true; else if (strcmp("-H", argv[argi]) == 0) print_header = false; else if (strcmp("-l", argv[argi]) == 0) print_leftover = true; else if (strcmp("-L", argv[argi]) == 0) print_leftover = false; else if (strcmp("-r", argv[argi]) == 0) print_parsedraw = true; else if (strcmp("-R", argv[argi]) == 0) print_parsedraw = false; else { usage(); fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]); bailOut(REGLOOKUP_EXIT_USAGE, ""); } } registry_file = argv[argi]; fd = openHive(registry_file); if(fd < 0) { fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file); bailOut(REGLOOKUP_EXIT_NOINPUT, ""); } if(print_verbose) regfi_log_set_mask(REGFI_LOG_ERROR|REGFI_LOG_WARN|REGFI_LOG_INFO); else regfi_log_set_mask(REGFI_LOG_ERROR); /* XXX: add command line option to choose output encoding */ f = regfi_alloc(fd, REGFI_ENCODING_ASCII); if(f == NULL) { close(fd); bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n"); } if(print_header) printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME," "NK_MTIME,NK_NVAL,VK_TYPE,VK_VALUE,VK_DATA_LEN," "SK_OWNER,SK_GROUP,SK_SACL,SK_DACL,RAW_CELL\n"); unalloc_cells = regfi_parse_unalloc_cells(f); if(unalloc_cells == NULL) { fprintf(stderr, "ERROR: Could not obtain list of unallocated cells.\n"); return 1; } unalloc_keys = range_list_new(); if(unalloc_keys == NULL) return 10; unalloc_linked_values = range_list_new(); if(unalloc_linked_values == NULL) return 10; unalloc_values = range_list_new(); if(unalloc_values == NULL) return 10; unalloc_sks = range_list_new(); if(unalloc_sks == NULL) return 10; ret = extractKeys(f, unalloc_cells, unalloc_keys); if(ret != 0) { fprintf(stderr, "ERROR: extractKeys() failed with %d.\n", ret); return ret; } ret = extractValueLists(f, unalloc_cells, unalloc_keys,unalloc_linked_values); if(ret != 0) { fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret); return ret; } /* Carve any orphan values */ ret = extractVKs(f, unalloc_cells, unalloc_values); if(ret != 0) { fprintf(stderr, "ERROR: extractVKs() failed with %d.\n", ret); return ret; } /* Carve any data associated with VK records */ ret = extractDataCells(f, unalloc_cells, unalloc_linked_values); if(ret != 0) { fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret); return ret; } ret = extractDataCells(f, unalloc_cells, unalloc_values); if(ret != 0) { fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret); return ret; } /* Carve any SK records */ ret = extractSKs(f, unalloc_cells, unalloc_sks); if(ret != 0) { fprintf(stderr, "ERROR: extractSKs() failed with %d.\n", ret); return ret; } /* Now that we're done carving, associate recovered keys with parents, * if at all possible. */ num_unalloc_keys = range_list_size(unalloc_keys); parent_paths = (char**)malloc(sizeof(char*)*num_unalloc_keys); if(parent_paths == NULL) return 10; for(i=0; i < num_unalloc_keys; i++) { cur_elem = range_list_get(unalloc_keys, i); tmp_key = (REGFI_NK*)cur_elem->data; if(tmp_key == NULL) return 20; parent_paths[i] = getParentPath(f, tmp_key); if(parent_paths[i] == NULL) return 20; } /* Now start the output */ for(i=0; i < num_unalloc_keys; i++) { cur_elem = range_list_get(unalloc_keys, i); tmp_key = (REGFI_NK*)cur_elem->data; printKey(f, tmp_key, parent_paths[i]); if(tmp_key->num_values > 0 && tmp_key->values != NULL) { /* XXX: Add command line option to choose output encoding */ regfi_interpret_keyname(f, tmp_key, true); tmp_name = get_quoted_keyname(tmp_key); tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2); if(tmp_path == NULL) { free(tmp_name); return 10; } sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name); for(j=0; j < tmp_key->values->num_values; j++) { tmp_value = (REGFI_VK*)range_list_find_data(unalloc_linked_values, tmp_key->values->elements[j] + REGFI_REGF_SIZE); if(tmp_value != NULL) printValue(f, tmp_value, tmp_path); } free(tmp_path); free(tmp_name); free(parent_paths[i]); } } free(parent_paths); /* Print out orphaned values */ for(i=0; i < range_list_size(unalloc_values); i++) { cur_elem = range_list_get(unalloc_values, i); tmp_value = (REGFI_VK*)cur_elem->data; printValue(f, tmp_value, ""); } if(print_leftover) { for(i=0; i < range_list_size(unalloc_cells); i++) { cur_elem = range_list_get(unalloc_cells, i); printCell(f, cur_elem->offset); } } range_list_free(unalloc_cells); range_list_free(unalloc_keys); range_list_free(unalloc_linked_values); range_list_free(unalloc_values); range_list_free(unalloc_sks); regfi_free(f); close(fd); return 0; } reglookup+git/doc/000775 001750 001750 00000000000 12566244653 015250 5ustar00sophiesophie000000 000000 reglookup+git/INSTALL000664 001750 001750 00000002051 12566244653 015532 0ustar00sophiesophie000000 000000 RegLookup Installation ====================== Prerequisites ------------- RegLookup and its associated libraries have the following build dependencies: - SCons (package "scons" in most popular distributions or from http://www.scons.org/) - GCC - talloc 2.x (under Debian, "libtalloc2" and "libtalloc-dev") - Python 2 (2.6+) or Python 3 (Python is required for SCons anyway) - Doxygen (optional, only needed to build developer documentation) Note that iconv support is required, as specified in IEEE Std 1003.1 (POSIX.1-2001). Some platforms still do not contain support for this natively, in which case you may need to install libiconv from: http://www.gnu.org/software/libiconv/ Survival Commands ----------------- scons # and as root scons install Advanced Installation --------------------- By default, most RegLookup files are installed in the appropriate directories under /usr/local. In order to override this behavior, set the PREFIX environment variable to the desired path. For example: $ PREFIX=/home/myuser/reglookup scons install reglookup+git/python/pyregfi/__init__.py000664 001750 001750 00000112440 12566244653 021604 0ustar00sophiesophie000000 000000 #!/usr/bin/env python # Copyright (C) 2010-2011 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 # 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: $ ## @package pyregfi # Python interface to the regfi library. # ## @mainpage API Documentation # # The pyregfi module provides a Python interface to the @ref regfi Windows # registry library. # # The library operates on registry hives, each of which is contained within a # single file. The quickest way to get started, is to use the @ref openHive() # function to obtain a Hive object. For example: # @code # >>> import pyregfi # >>> myHive = pyregfi.openHive('/mnt/win/c/WINDOWS/system32/config/system') # @endcode # # Using this Hive object, one can begin investigating what top-level keys # exist by starting with the root Key attribute: # @code # >>> for key in myHive.root.subkeys: # ... print(key.name) # ControlSet001 # ControlSet003 # LastKnownGoodRecovery # MountedDevices # Select # Setup # WPA # @endcode # # From there, accessing subkeys and values by name is a simple matter of: # @code # >>> myKey = myHive.root.subkeys['Select'] # >>> myValue = myKey.values['Current'] # @endcode # # The data associated with a Value can be obtained through the fetch_data() # method: # @code # >>> print(myValue.fetch_data()) # 1 # @endcode # # While useful for simple exercises, using the subkeys object for deeply nested # paths is not efficient and doesn't make for particularly attractive code. # Instead, a special-purpose HiveIterator class is provided for simplicity of # use and fast access to specific known paths: # @code # >>> myIter = pyregfi.HiveIterator(myHive) # >>> myIter.descend(['ControlSet001','Control','NetworkProvider','HwOrder']) # >>> myKey = myIter.current_key() # >>> print(myKey.values['ProviderOrder'].fetch_data()) # RDPNP,LanmanWorkstation,WebClient # @endcode # # The first two lines above can be simplified in some "syntactic sugar" provided # by the Hive.subtree() method. Also, as one might expect, the HiveIterator # also acts as an iterator, producing keys in a depth-first order. # For instance, to traverse all keys under the ControlSet003\\Services key, # printing their names as we go, we could do: # @code # >>> for key in Hive.subtree(['ControlSet003','Services']): # >>> print(key.name) # Services # Abiosdsk # abp480n5 # Parameters # PnpInterface # ACPI # [...] # @endcode # # Note that "Services" was printed first, since the subtree is traversed as a # "preordering depth-first" search starting with the HiveIterator's current_key(). # As one might expect, traversals of subtrees stops when all elements in a # specific subtree (and none outside of it) have been traversed. # # For more information, peruse the various attributes and methods available on # the Hive, HiveIterator, Key, Value, and Security classes. # # @note @ref regfi is a read-only library by design and there # are no plans to implement write support. # # @note At present, pyregfi has been tested with Python versions 2.6 and 3.1 # # @note Developers strive to make pyregfi thread-safe. # import sys import time import ctypes import ctypes.util import threading from pyregfi.structures import * ## An enumeration of registry Value data types # # @note This is a static class, there is no need to instantiate it. # Just access its attributes directly as DATA_TYPES.SZ, etc class DATA_TYPES(object): # XXX: add dictionary lookup attributes to convert both directions between # the integers and typenames ## None / Unknown NONE = 0 ## String SZ = 1 ## String with %...% expansions EXPAND_SZ = 2 ## Binary buffer BINARY = 3 ## 32 bit integer (little endian) DWORD = 4 # DWORD, little endian ## 32 bit integer (little endian) DWORD_LE = 4 ## 32 bit integer (big endian) DWORD_BE = 5 # DWORD, big endian ## Symbolic link LINK = 6 ## List of strings MULTI_SZ = 7 ## Unknown structure RESOURCE_LIST = 8 ## Unknown structure FULL_RESOURCE_DESCRIPTOR = 9 ## Unknown structure RESOURCE_REQUIREMENTS_LIST = 10 ## 64 bit integer QWORD = 11 # 64-bit little endian ## An enumeration of log message types # # @note This is a static class, there is no need to instantiate it. # Just access its attributes directly as LOG_TYPES.INFO, etc class LOG_TYPES(object): ## Informational messages, useful in debugging INFO = 0x01 ## Non-critical problems in structure parsing or intepretation WARN = 0x04 ## Major failures ERROR = 0x10 def _buffer2bytearray(char_pointer, length): if length == 0 or char_pointer == None: return None ret_val = bytearray(length) for i in range(0,length): ret_val[i] = char_pointer[i][0] return ret_val def _strlist2charss(str_list): ret_val = [] for s in str_list: ret_val.append(s.encode('utf-8', 'replace')) ret_val = (ctypes.c_char_p*(len(str_list)+1))(*ret_val) # Terminate the char** with a NULL pointer ret_val[-1] = 0 return ret_val def _charss2strlist(chars_pointer): ret_val = [] i = 0 s = chars_pointer[i] while s: ret_val.append(s.decode('utf-8', 'replace')) i += 1 s = chars_pointer[i] return ret_val ## Returns the (py)regfi library version # # @return A string indicating the version def getVersion(): return regfi.regfi_version() ## Retrieves messages produced by regfi during parsing and interpretation # # The regfi C library may generate log messages stored in a special thread-safe # global data structure. These messages should be retrieved periodically or # after each major operation by callers to determine if any errors or warnings # should be reported to the user. Failure to retrieve these could result in # excessive memory consumption. def getLogMessages(): msgs = regfi.regfi_log_get_str() if not msgs: return '' return msgs.decode('utf-8') ## Sets the types of log messages to record # # @param log_types A sequence of message types that regfi should generate. # Message types can be found in the LOG_TYPES enumeration. # # @return True on success, False on failure. Failures are rare, but could # indicate that global logging is not operating as expected. # # Example: # @code # setLogMask((LOG_TYPES.ERROR, LOG_TYPES.WARN, LOG_TYPES.INFO)) # @endcode # # The message mask is a global (all hives, iterators), thread-specific value. # For more information, see @ref regfi_log_set_mask. # def setLogMask(log_types): mask = 0 for m in log_types: mask |= m return regfi.regfi_log_set_mask(mask) ## Opens a file as a registry hive # # @param path The file path of a hive, as one would provide to the # open() built-in # # @return A new Hive instance def openHive(path): fh = open(path, 'rb') return Hive(fh) ## Abstract class for most objects returned by the library class _StructureWrapper(object): _hive = None _base = None def __init__(self, hive, base): if not hive: raise Exception("Could not create _StructureWrapper," + " hive is NULL. Current log:\n" + getLogMessages()) if not base: raise Exception("Could not create _StructureWrapper," + " base is NULL. Current log:\n" + getLogMessages()) self._hive = hive self._base = base # Memory management for most regfi structures is taken care of here def __del__(self): if self._base: regfi.regfi_free_record(self._hive.file, self._base) # Any attribute requests not explicitly defined in subclasses gets passed # to the equivalent REGFI_* structure defined in structures.py def __getattr__(self, name): return getattr(self._base.contents, name) ## Test for equality # # Records returned by pyregfi may be compared with one another. For example: # @code # >>> key2 = key1.subkeys['child'] # >>> key1 == key2 # False # >>> key1 != key2 # True # >>> key1 == key2.get_parent() # True # @endcode def __eq__(self, other): return (type(self) == type(other)) and (self.offset == other.offset) def __ne__(self, other): return (not self.__eq__(other)) class Key(): pass class Value(): pass ## Represents a registry SK record which contains a security descriptor # class Security(_StructureWrapper): ## Number of registry Keys referencing this SK record ref_count = 1 ## The absolute file offset of the SK record's cell in the Hive file offset = 0xCAFEBABE ## The @ref winsec.SecurityDescriptor for this SK record descriptor = object() def __init__(self, hive, base): super(Security, self).__init__(hive, base) # XXX: add checks for NULL pointers self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents) ## Loads the "next" Security record in the hive # # @note # SK records are included in a circular, doubly-linked list. # To iterate over all SK records, be sure to check for the repetition of # the SK record you started with to determine when all have been traversed. def next_security(self): return Security(self._hive, regfi.regfi_next_sk(self._hive.file, self._base)) ## Loads the "previous" Security record in the hive # # @note # SK records are included in a circular, doubly-linked list. # To iterate over all SK records, be sure to check for the repetition of # the SK record you started with to determine when all have been traversed. def prev_security(self): return Security(self._hive, regfi.regfi_prev_sk(self._hive.file, self._base)) ## Abstract class for ValueList and SubkeyList class _GenericList(object): # XXX: consider implementing keys(), values(), items() and other dictionary methods _hive = None _key_base = None _length = None _current = None # implementation-specific functions for SubkeyList and ValueList _fetch_num = None _find_element = None _get_element = None _constructor = None def __init__(self, key): if not key: raise Exception("Could not create _GenericList; key is NULL." + "Current log:\n" + getLogMessages()) base = regfi.regfi_reference_record(key._hive.file, key._base) if not base: raise Exception("Could not create _GenericList; memory error." + "Current log:\n" + getLogMessages()) self._key_base = cast(base, type(key._base)) self._length = self._fetch_num(self._key_base) self._hive = key._hive def __del__(self): regfi.regfi_free_record(self._hive.file, self._key_base) ## Length of list def __len__(self): return self._length ## Retrieves a list element by name # # @param name The name of the subkey or value desired. # This is case-insensitive. # # @note The registry format does not inherently prevent multiple # subkeys or values from having the same name, having a key # and a value with the same name, or having the same name in # different cases that could both match. # This interface simply returns the first match in the list. # Lookups using this method could also fail due to incorrectly # encoded strings stored as names. # To identify any duplicates or elements with malformed names, # use the iterator interface to check every list element. # # @return the first element whose name matches, or None if the element # could not be found def __getitem__(self, name): # XXX: Consider interpreting integer names as offsets in the underlying list index = ctypes.c_uint32() if isinstance(name, str): name = name.encode('utf-8') if name != None: name = create_string_buffer(bytes(name)) if self._find_element(self._hive.file, self._key_base, name, byref(index)): return self._constructor(self._hive, self._get_element(self._hive.file, self._key_base, index)) raise KeyError('') ## Fetches the requested element by name, or the default value if the lookup # fails. # def get(self, name, default): try: return self[name] except KeyError: return default def __iter__(self): self._current = 0 return self def __next__(self): if self._current >= self._length: raise StopIteration('') elem = self._get_element(self._hive.file, self._key_base, ctypes.c_uint32(self._current)) self._current += 1 return self._constructor(self._hive, elem) # For Python 2.x next = __next__ ## The list of subkeys associated with a Key # # This attribute is both iterable: # @code # for k in myKey.subkeys: # ... # @endcode # and accessible as a dictionary: # @code # mySubkey = myKey.subkeys["keyName"] # @endcode # # You may also request the len() of a subkeys list. # However keys(), values(), items() and similar methods are not currently # implemented. class SubkeyList(_GenericList): _fetch_num = regfi.regfi_fetch_num_subkeys _find_element = regfi.regfi_find_subkey _get_element = regfi.regfi_get_subkey ## The list of values associated with a Key # # This attribute is both iterable: # @code # for v in myKey.values: # ... # @endcode # and accessible as a dictionary: # @code # myValue = myKey.values["valueName"] # @endcode # # You may also request the len() of a values list. # However keys(), values(), items() and similar methods are not currently # implemented. class ValueList(_GenericList): _fetch_num = regfi.regfi_fetch_num_values _find_element = regfi.regfi_find_value _get_element = regfi.regfi_get_value ## Registry key # These represent registry keys (@ref REGFI_NK records) and provide # access to their subkeys, values, and other metadata. # # @note Key instances may provide access to more attributes than are # documented here. However, undocumented attributes may change over time # and are not officially supported. If you need access to an attribute # not shown here, see @ref pyregfi.structures. class Key(_StructureWrapper): ## A @ref ValueList object representing the list of Values # stored on this Key values = None ## A @ref SubkeyList object representing the list of subkeys # stored on this Key subkeys = None ## The raw Key name as an uninterpreted bytearray name_raw = (b"...") ## The name of the Key as a (unicode) string name = "..." ## The string encoding used to store the Key's name ("ascii" or "utf-16-le") name_encoding = "ascii" ## The absolute file offset of the Key record's cell in the Hive file offset = 0xCAFEBABE ## This Key's last modified time represented as the number of seconds # since the UNIX epoch in UTC; similar to what time.time() returns modified = 1300000000.123456 ## The NK record's flags field flags = 0x10110001 def __init__(self, hive, base): super(Key, self).__init__(hive, base) self.values = ValueList(self) self.subkeys = SubkeyList(self) def __getattr__(self, name): if name == "name": ret_val = super(Key, self).__getattr__(name) if not ret_val: ret_val = self.name_raw if ret_val != None: ret_val = ret_val.decode(self.name_encoding, 'replace') else: ret_val = ret_val.decode('utf-8', 'replace') elif name == "name_encoding": flags = super(Key, self).__getattr__("flags") if (flags & structures.REGFI_NK_FLAG_ASCIINAME) > 0: ret_val = "ascii" ret_val = "utf-16-le" elif name == "name_raw": ret_val = super(Key, self).__getattr__(name) length = super(Key, self).__getattr__('name_length') ret_val = _buffer2bytearray(ret_val, length) elif name == "modified": ret_val = regfi.regfi_nt2unix_time(self._base.contents.mtime) else: ret_val = super(Key, self).__getattr__(name) return ret_val ## Retrieves the Security properties for this key def fetch_security(self): return Security(self._hive, regfi.regfi_fetch_sk(self._hive.file, self._base)) ## Retrieves the class name for this key # # Class names are typically stored as UTF-16LE strings, so these are decoded # into proper python (unicode) strings. However, if this fails, a bytearray # is instead returned containing the raw buffer stored for the class name. # # @return The class name as a string or bytearray. None if a class name # doesn't exist or an unrecoverable error occurred during retrieval. def fetch_classname(self): ret_val = None cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base) if cn_p: cn_struct = cn_p.contents if cn_struct.interpreted: ret_val = cn_struct.interpreted.decode('utf-8', 'replace') else: ret_val = _buffer2bytearray(cn_struct.raw, cn_struct.size) regfi.regfi_free_record(self._hive.file, cn_p) return ret_val ## Retrieves this key's parent key # # @return The parent's Key instance or None if current key is root # (or an error occured) def get_parent(self): if self.is_root(): return None parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base) if parent_base: return Key(self._hive, parent_base) return None ## Checks to see if this Key is the root of its Hive # # @return True if it is, False otherwise def is_root(self): return (self._hive.root == self) ## Registry value (metadata) # # These represent registry values (@ref REGFI_VK records) and provide # access to their associated data. # # @note Value instances may provide access to more attributes than are # documented here. However, undocumented attributes may change over time # and are not officially supported. If you need access to an attribute # not shown here, see @ref pyregfi.structures. class Value(_StructureWrapper): ## The raw Value name as an uninterpreted bytearray name_raw = (b"...") ## The name of the Value as a (unicode) string name = "..." ## The string encoding used to store the Value's name ("ascii" or "utf-16-le") name_encoding = "ascii" ## The absolute file offset of the Value record's cell in the Hive file offset = 0xCAFEBABE ## The length of data advertised in the VK record data_size = 0xCAFEBABE ## An integer which represents the data type for this Value's data # Typically this value is one of 12 types defined in @ref DATA_TYPES, # but in some cases (the SAM hive) it may be used for other purposes type = DATA_TYPES.NONE ## The VK record's flags field flags = 0x10110001 ## Retrieves the Value's data according to advertised type # # Data is loaded from its cell(s) and then interpreted based on the data # type recorded in the Value. It is not uncommon for data to be stored with # the wrong type or even with invalid types. If you have difficulty # obtaining desired data here, use @ref fetch_raw_data(). # # @return The interpreted representation of the data as one of several # possible Python types, as listed below. None if any failure # occurred during extraction or conversion. # # @retval string for SZ, EXPAND_SZ, and LINK # @retval int for DWORD, DWORD_BE, and QWORD # @retval list(string) for MULTI_SZ # @retval bytearray for NONE, BINARY, RESOURCE_LIST, # FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST # def fetch_data(self): ret_val = None data_p = regfi.regfi_fetch_data(self._hive.file, self._base) if not data_p: return None data_struct = data_p.contents if data_struct.interpreted_size == 0: ret_val = None elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK): # Unicode strings ret_val = data_struct.interpreted.string.decode('utf-8', 'replace') elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE): # 32 bit integers ret_val = data_struct.interpreted.dword elif data_struct.type == DATA_TYPES.QWORD: # 64 bit integers ret_val = data_struct.interpreted.qword elif data_struct.type == DATA_TYPES.MULTI_SZ: ret_val = _charss2strlist(data_struct.interpreted.multiple_string) elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST, DATA_TYPES.FULL_RESOURCE_DESCRIPTOR, DATA_TYPES.RESOURCE_REQUIREMENTS_LIST, DATA_TYPES.BINARY): ret_val = _buffer2bytearray(data_struct.interpreted.none, data_struct.interpreted_size) regfi.regfi_free_record(self._hive.file, data_p) return ret_val ## Retrieves raw representation of Value's data # # @return A bytearray containing the data # def fetch_raw_data(self): ret_val = None # XXX: should we load the data without interpretation instead? data_p = regfi.regfi_fetch_data(self._hive.file, self._base) if not data_p: return None data_struct = data_p.contents ret_val = _buffer2bytearray(data_struct.raw, data_struct.size) regfi.regfi_free_record(self._hive.file, data_p) return ret_val def __getattr__(self, name): ret_val = None if name == "name": ret_val = super(Value, self).__getattr__(name) if not ret_val: ret_val = self.name_raw if ret_val != None: ret_val = ret_val.decode(self.name_encoding, 'replace') else: ret_val = ret_val.decode('utf-8', 'replace') elif name == "name_encoding": flags = super(Value, self).__getattr__("flags") if (flags & structures.REGFI_VK_FLAG_ASCIINAME) > 0: ret_val = "ascii" else: ret_val = "utf-16-le" elif name == "name_raw": ret_val = super(Value, self).__getattr__(name) length = super(Value, self).__getattr__('name_length') ret_val = _buffer2bytearray(ret_val, length) else: ret_val = super(Value, self).__getattr__(name) return ret_val # Avoids chicken/egg class definitions. # Also makes for convenient code reuse in these lists' parent classes. SubkeyList._constructor = Key ValueList._constructor = Value ## Represents a single registry hive (file) class Hive(): file = None raw_file = None _fh = None #_root = None ## The root Key of this Hive root = None ## This Hives's last modified time represented as the number of seconds # since the UNIX epoch in UTC; similar to what time.time() returns modified = 1300000000.123456 ## First sequence number sequence1 = 12345678 ## Second sequence number sequence2 = 12345678 ## Major version major_version = 1 ## Minor version minor_version = 5 ## Constructor # # Initialize a new Hive based on a Python file object. To open a file by # path, see @ref openHive. # # @param fh A Python file object. The constructor first looks for a valid # fileno attribute on this object and uses it if possible. # Otherwise, the seek and read methods are used for file # access. # # @note Supplied file must be seekable. Do not perform any operation on # the provided file object while a Hive is using it. Do not # construct multiple Hive instances from the same file object. # If a file must be accessed by separate code and pyregfi # simultaneously, use a separate file descriptor. Hives are # thread-safe, so multiple threads may use a single Hive object. def __init__(self, fh): # The fileno method may not exist, or it may throw an exception # when called if the file isn't backed with a descriptor. self._fh = fh fn = None try: # XXX: Native calls to Windows filenos don't seem to work. # Need to investigate why. if not is_win32 and hasattr(fh, 'fileno'): fn = fh.fileno() except: pass if fn != None: self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8) if not self.file: # XXX: switch to non-generic exception raise Exception("Could not open registry file. Current log:\n" + getLogMessages()) else: fh.seek(0) self.raw_file = structures.REGFI_RAW_FILE() self.raw_file.fh = fh self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek) self.raw_file.read = read_cb_type(self.raw_file.cb_read) self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8) if not self.file: # XXX: switch to non-generic exception raise Exception("Could not open registry file. Current log:\n" + getLogMessages()) def __getattr__(self, name): if name == "root": # XXX: This creates reference loops. Need to cache better inside regfi #if self._root == None: # self._root = Key(self, regfi.regfi_get_rootkey(self.file)) #return self._root return Key(self, regfi.regfi_get_rootkey(self.file)) elif name == "modified": return regfi.regfi_nt2unix_time(self._base.contents.mtime) return getattr(self.file.contents, name) def __del__(self): if self.file: regfi.regfi_free(self.file) def __iter__(self): return HiveIterator(self) ## Creates a @ref HiveIterator initialized at the specified path in # the hive. # # @param path A list of Key names which represent an absolute path within # the Hive # # @return A @ref HiveIterator which is positioned at the specified path. # # @exception Exception If the path could not be found/traversed def subtree(self, path): hi = HiveIterator(self) hi.descend(path) return hi ## A special purpose iterator for registry hives # # Iterating over an object of this type causes all keys in a specific # hive subtree to be returned in a depth-first manner. These iterators # are typically created using the @ref Hive.subtree() function on a @ref Hive # object. # # HiveIterators can also be used to manually traverse up and down a # registry hive as they retain information about the current position in # the hive, along with which iteration state for subkeys and values for # every parent key. See the @ref up and @ref down methods for more # information. class HiveIterator(): _hive = None _iter = None _iteration_root = None _lock = None def __init__(self, hive): self._iter = regfi.regfi_iterator_new(hive.file) if not self._iter: raise Exception("Could not create iterator. Current log:\n" + getLogMessages()) self._hive = hive self._lock = threading.RLock() def __getattr__(self, name): self._lock.acquire() ret_val = getattr(self._iter.contents, name) self._lock.release() return ret_val def __del__(self): self._lock.acquire() regfi.regfi_iterator_free(self._iter) self._lock.release() def __iter__(self): self._lock.acquire() self._iteration_root = None self._lock.release() return self def __next__(self): self._lock.acquire() if self._iteration_root == None: self._iteration_root = self.current_key().offset elif not regfi.regfi_iterator_down(self._iter): up_ret = regfi.regfi_iterator_up(self._iter) while (up_ret and not regfi.regfi_iterator_next_subkey(self._iter)): if self._iteration_root == self.current_key().offset: self._iteration_root = None self._lock.release() raise StopIteration('') up_ret = regfi.regfi_iterator_up(self._iter) if not up_ret: self._iteration_root = None self._lock.release() raise StopIteration('') # XXX: Use non-generic exception if not regfi.regfi_iterator_down(self._iter): self._lock.release() raise Exception('Error traversing iterator downward.'+ ' Current log:\n'+ getLogMessages()) regfi.regfi_iterator_first_subkey(self._iter) ret_val = self.current_key() self._lock.release() return ret_val # For Python 2.x next = __next__ # XXX: Should add sanity checks on some of these traversal functions # to throw exceptions if a traversal/retrieval *should* have worked # but failed for some reason. ## Descends the iterator to a subkey # # Descends the iterator one level to the current subkey, or a subkey # specified by name. # # @param subkey_name If specified, locates specified subkey by name # (via find_subkey()) and descends to it. # # @return True if successful, False otherwise def down(self, subkey_name=None): ret_val = None if subkey_name == None: self._lock.acquire() ret_val = regfi.regfi_iterator_down(self._iter) else: if name != None: name = name.encode('utf-8') self._lock.acquire() ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name) and regfi.regfi_iterator_down(self._iter)) self._lock.release() return ret_val ## Causes the iterator to ascend to the current Key's parent # # @return True if successful, False otherwise # # @note The state of current subkeys and values at this level in the tree # is lost as a side effect. That is, if you go up() and then back # down() again, current_subkey() and current_value() will return # default selections. def up(self): self._lock.acquire() ret_val = regfi.regfi_iterator_up(self._iter) self._lock.release() return ret_val ## Selects first subkey of current key # # @return A Key instance for the first subkey. # None on error or if the current key has no subkeys. def first_subkey(self): ret_val = None self._lock.acquire() if regfi.regfi_iterator_first_subkey(self._iter): ret_val = self.current_subkey() self._lock.release() return ret_val ## Selects first value of current Key # # @return A Value instance for the first value. # None on error or if the current key has no values. def first_value(self): ret_val = None self._lock.acquire() if regfi.regfi_iterator_first_value(self._iter): ret_val = self.current_value() self._lock.release() return ret_val ## Selects the next subkey in the current Key's list # # @return A Key instance for the next subkey. # None if there are no remaining subkeys or an error occurred. def next_subkey(self): ret_val = None self._lock.acquire() if regfi.regfi_iterator_next_subkey(self._iter): ret_val = self.current_subkey() self._lock.release() return ret_val ## Selects the next value in the current Key's list # # @return A Value instance for the next value. # None if there are no remaining values or an error occurred. def next_value(self): ret_val = None self._lock.acquire() if regfi.regfi_iterator_next_value(self._iter): ret_val = self.current_value() self._lock.release() return ret_val ## Selects the first subkey which has the specified name # # @return A Key instance for the selected key. # None if it could not be located or an error occurred. def find_subkey(self, name): if name != None: name = name.encode('utf-8') ret_val = None self._lock.acquire() if regfi.regfi_iterator_find_subkey(self._iter, name): ret_val = self.current_subkey() self._lock.release() return ret_val ## Selects the first value which has the specified name # # @return A Value instance for the selected value. # None if it could not be located or an error occurred. def find_value(self, name): if name != None: name = name.encode('utf-8') ret_val = None self._lock.acquire() if regfi.regfi_iterator_find_value(self._iter, name): ret_val = self.current_value() self._lock.release() return ret_val ## Retrieves the currently selected subkey # # @return A Key instance of the current subkey def current_subkey(self): self._lock.acquire() ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter)) self._lock.release() return ret_val ## Retrieves the currently selected value # # @return A Value instance of the current value def current_value(self): self._lock.acquire() ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter)) self._lock.release() return ret_val ## Retrieves the current key # # @return A Key instance of the current position of the iterator def current_key(self): self._lock.acquire() ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter)) self._lock.release() return ret_val ## Traverse downward multiple levels # # This is more efficient than calling down() multiple times # # @param path A list of Key names which represent the path to descend # # @exception Exception If path could not be located def descend(self, path): cpath = _strlist2charss(path) self._lock.acquire() result = regfi.regfi_iterator_descend(self._iter, cpath) self._lock.release() if not result: # XXX: Use non-generic exception raise Exception('Could not locate path.\n'+getLogMessages()) ## Obtains a list of the current key's ancestry # # @return A list of all parent keys starting with the root Key and ending # with the current Key def ancestry(self): self._lock.acquire() result = regfi.regfi_iterator_ancestry(self._iter) self._lock.release() ret_val = [] i = 0 k = result[i] while k: k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK)) ret_val.append(Key(self._hive, k)) i += 1 k = result[i] regfi.regfi_free_record(self._hive.file, result) return ret_val ## Obtains the current path of the iterator # # @return A list of key names starting with the root up to and # including the current key # def current_path(self): ancestry = self.ancestry() return [a.name for a in ancestry] # Freeing symbols defined for the sake of documentation del Value.name,Value.name_encoding,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags del Key.name,Key.name_encoding,Key.name_raw,Key.offset,Key.modified,Key.flags del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version del Security.ref_count,Security.offset,Security.descriptor reglookup+git/regfi_version.py000664 001750 001750 00000000133 12566244653 017713 0ustar00sophiesophie000000 000000 SVN_REV = "$Rev$" LATEST='1.0.1' REGFI_VERSION = "%s.%s" % (LATEST, SVN_REV.split(' ')[1]) reglookup+git/doc/devel/TODO000664 001750 001750 00000005031 12566244653 017036 0ustar00sophiesophie000000 000000 $Id$ 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. - reglookup-timeline needs to be replaced with something cross-platform. Perhaps a python script that provides MTIME range filtering capabilities. - Need to integrate much of reglookup-recover's algorithms into regfi and then expose them from the bottom-up to provide building blocks through regfi and pyregfi. This should be addressed along with code to support handling of partial/fragmented registry hives. - Testing, testing, and more testing. reglookup needs to be more heavily tested on all recent Windows platforms. 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. - Unicode support still needs improvement. While parsing strings seems to be decent, UTF-8 output would be nice. - Continue to improve regfi/pyregfi APIs as needed. winsec library needs more flexibility and documentation. - Consider adding regfi wrappers for other high-level languages (perl? ruby?). - Documentation. The security descriptor output format needs to be documented. Also, function contracts should be added to the lower-level functions of regfi.c. Continue adding - Consider switching from libiconv to Joachim Metz's libuna for increased portability and easier builds. - Grep through the source for 'XXX', and you'll find more. - Consider integrating packaging rules for debian/other platforms into trunk. - Investigate why file descriptors can't be directly used in Windows 1.0 RELEASE =========== Testing Full diffs regfi and pyregfi threading valgrind in multiple scenarios for reglookup, reglookup-recover double check man pages reglookup+git/include/void_stack.h000664 001750 001750 00000011311 12566244653 020422 0ustar00sophiesophie000000 000000 /* * 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 * 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$ */ /** *@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 #include #include #include #include #include "compat.h" /** XXX: document this. */ typedef struct _void_stack { void** elements; unsigned short max_size; unsigned short top; } void_stack; /** XXX: document this. */ typedef struct _void_stack_iterator { const void_stack* stack; unsigned short cur; } void_stack_iterator; /** Allocates a new void_stack. * * @param max_size the maxiumum number of elements * which may be pushed onto the stack. * * @return a pointer to the newly allocated void_stack, * or NULL if an error occurred. */ _EXPORT() void_stack* void_stack_new(unsigned short max_size); /** Makes a shallow copy of void_stack. * * @param v the stack to make a copy of. * * @return a pointer to the duplicate void_stack, or NULL if an error occurred. */ _EXPORT() void_stack* void_stack_copy(const void_stack* v); /** Makes a shallow copy of void_stack in reverse order. * * @param v the stack to make a copy of. * * @return a pointer to the duplicate void_stack * (which will be in reverse order), or NULL if an error occurred. */ _EXPORT() void_stack* void_stack_copy_reverse(const void_stack* v); /** Frees the memory associated with a void_stack, but not the elements held * on the stack. * * @param stack the stack to be free()d. */ _EXPORT() void void_stack_free(void_stack* stack); /** 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. * * @param stack the stack to be free()d. */ _EXPORT() void void_stack_free_deep(void_stack* stack); /** Query the current number of elements on a void_stack() * * @param stack the void_stack to query * * @return the number of elements currently on the stack. */ _EXPORT() unsigned short void_stack_size(const void_stack* stack); /** Removes the top element on a void_stack and returns a reference to it. * * @param stack the void_stack to pop * * @return a pointer to the popped stack element, or NULL if no elements exist * on the stack. */ _EXPORT() void* void_stack_pop(void_stack* stack); /** Puts a new element on the top of a void_stack. * * @param stack the void_stack being modified. * @param e the element to be added * * @return true if the element was successfully added, false otherwise. */ _EXPORT() bool void_stack_push(void_stack* stack, void* e); /** Returns a pointer to the current element on the top of the stack. * * @param stack the void_stack being queried. * * @return a pointer to the current element on the top of the stack, or NULL if * no elements exist in the stack. */ _EXPORT() const void* void_stack_cur(const void_stack* stack); /** Creates a new iterator for the specified void_stack. * * @param stack the void_stack to be referenced by the new iterator * * @return a new void_stack_iterator, or NULL if an error occurred. */ _EXPORT() void_stack_iterator* void_stack_iterator_new(const void_stack* stack); /** Frees a void_stack_iterator. * * Does not affect the void_stack referenced by the iterator. * * @param iter the void_stack_iterator to be free()d. */ _EXPORT() void void_stack_iterator_free(void_stack_iterator* iter); /** 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). * * @param iter the void_stack_iterator used to lookup the next element. * * @return a pointer to the next element. */ _EXPORT() const void* void_stack_iterator_next(void_stack_iterator* iter); /* XXX: for completeness, might want to add a void_stack_iterator_first() * function, to return iterator to first element */ #endif reglookup+git/python/000775 001750 001750 00000000000 12566244653 016024 5ustar00sophiesophie000000 000000 reglookup+git/python/experimental/lexer.py000664 001750 001750 00000016164 12566244653 022222 0ustar00sophiesophie000000 000000 #!/usr/bin/env python # ****************************************************** # Michael Cohen # # ****************************************************** # Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$ # ****************************************************** # # * This program is free software; you can redistribute it and/or # * modify it under the terms of the GNU General Public License # * as published by the Free Software Foundation; either version 2 # * of the License, or (at your option) any later version. # * # * This program is distributed in the hope that it will be useful, # * but WITHOUT ANY WARRANTY; without even the implied warranty of # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # * GNU General Public License for more details. # * # * You should have received a copy of the GNU General Public License # * along with this program; if not, write to the Free Software # * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ****************************************************** """ A simple feed lexer. """ import re,sys class Lexer: """ A generic feed lexer """ ## The following is a description of the states we have and the ## way we move through them: format is an array of ## [ state_re, re, token/action, next state ] tokens = [] state = "INITIAL" buffer = '' error = 0 verbose = 0 state_stack = [] processed = 0 processed_buffer = '' saved_state = None flags = 0 def __init__(self, verbose=0, fd=None): if not self.verbose: self.verbose = verbose if len(self.tokens[0])==4: for row in self.tokens: row.append(re.compile(row[0], re.DOTALL)) row.append(re.compile(row[1], re.DOTALL | re.M | re.S | self.flags )) self.fd = fd def save_state(self, t=None, m=None): """ Returns a dict which represents the current state of the lexer. When provided to restore_state, the lexer is guaranteed to be in the same state as when the save_state was called. Note that derived classes may need to extend this. """ ## Cant save our state if we have errors. We need to guarantee ## that we rewind to a good part of the file. if self.error: return try: end = m.end() except: end = 0 self.saved_state = dict(state_stack = self.state_stack[:], processed = self.processed - end, processed_buffer = self.processed_buffer, readptr = self.fd.tell() - len(self.buffer) - end, state = self.state, objects = self.objects[:], error = self.error, ) if self.verbose>1: print "Saving state %s" % self.processed def restore_state(self): state = self.saved_state if not state: return self.state_stack = state['state_stack'] self.processed = state['processed'] self.processed_buffer = state['processed_buffer'] self.buffer = '' self.fd.seek(state['readptr']) self.state = state['state'] self.objects = state['objects'] self.error = state['error'] if self.verbose>1: print "Restoring state to offset %s" % self.processed def next_token(self, end = True): ## Now try to match any of the regexes in order: current_state = self.state for state_re, re_str, token, next, state, regex in self.tokens: ## Does the rule apply for us now? if state.match(current_state): if self.verbose > 2: print "%s: Trying to match %r with %r" % (self.state, self.buffer[:10], re_str) m = regex.match(self.buffer) if m: if self.verbose > 3: print "%s matched %s" % (re_str, m.group(0).encode("utf8")) ## The match consumes the data off the buffer (the ## handler can put it back if it likes) self.processed_buffer += self.buffer[:m.end()] self.buffer = self.buffer[m.end():] self.processed += m.end() ## Try to iterate over all the callbacks specified: for t in token.split(','): try: if self.verbose > 0: print "0x%X: Calling %s %r" % (self.processed, t, m.group(0)) cb = getattr(self, t, self.default_handler) except AttributeError: continue ## Is there a callback to handle this action? next_state = cb(t, m) if next_state == "CONTINUE": continue elif next_state: next = next_state self.state = next if next: self.state = next return token ## Check that we are making progress - if we are too full, we ## assume we are stuck: if end and len(self.buffer)>0 or len(self.buffer)>1024: self.processed_buffer += self.buffer[:1] self.buffer = self.buffer[1:] self.ERROR("Lexer Stuck, discarding 1 byte (%r) - state %s" % (self.buffer[:10], self.state)) return "ERROR" ## No token were found return None def feed(self, data): self.buffer += data def empty(self): return not len(self.buffer) def default_handler(self, token, match): if self.verbose > 2: print "Default handler: %s with %r" % (token,match.group(0)) def ERROR(self, message = None, weight =1): if self.verbose > 0 and message: print "Error(%s): %s" % (weight,message) self.error += weight def PUSH_STATE(self, token = None, match = None): if self.verbose > 1: print "Storing state %s" % self.state self.state_stack.append(self.state) def POP_STATE(self, token = None, match = None): try: state = self.state_stack.pop() if self.verbose > 1: print "Returned state to %s" % state return state except IndexError: print "Tried to pop the state but failed - possible recursion error" return None def close(self): """ Just a conveniece function to force us to parse all the data """ while self.next_token(): pass class SelfFeederMixIn(Lexer): """ This mixin is used to make a lexer which feeds itself one sector at the time. Note that self.fd must be the fd we read from. """ def parse_fd(self, fd): self.feed(fd.read()) while self.next_token(): pass reglookup+git/python/experimental/regfi/regfi.c000664 001750 001750 00000023631 12566244653 023062 0ustar00sophiesophie000000 000000 /* * pyregfi/libregfi glue code * * Copyright (C) 2010 Michael I. Cohen * Copyright (C) 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 * 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: $ */ #include "pyregfi.h" static int RegistryFile_dest(void *self) { RegistryFile this = (RegistryFile)self; regfi_free(this->reg); close(this->fd); return 0; } static RegistryFile RegistryFile_Con(RegistryFile self, char *filename) { self->fd = open(filename, O_RDONLY); if(self->fd < 0) { RaiseError(EIOError, "Unable to open %s", filename); goto error; } self->reg = regfi_alloc(self->fd); if(!self->reg) { RaiseError(ERuntimeError, "REGFI Error: %s", regfi_log_get_str()); /*char* e = regfi_log_get_str();*/ /*fprintf(stderr, "%p\n", e);*/ goto error; } talloc_set_destructor((void *)self, RegistryFile_dest); return self; error: talloc_free(self); return NULL; } static TreeIterator RegistryFile_TreeIterator(RegistryFile self, char **path, REGFI_ENCODING encoding) { return CONSTRUCT(TreeIterator, TreeIterator, Con, NULL, self, path, encoding); } VIRTUAL(RegistryFile, Object) { VMETHOD(Con) = RegistryFile_Con; VMETHOD(TreeIterator) = RegistryFile_TreeIterator; } END_VIRTUAL static int RegistryKey_dest(void *self) { RegistryKey this = (RegistryKey)self; talloc_unlink(this, (void*)this->key); return 0; } static RegistryKey RegistryKey_Con(RegistryKey self, RegistryFile file, REGFI_NK* base_key) { if(base_key == NULL) goto error; self->key = base_key; self->file = file; talloc_reference(self, self->key); talloc_set_destructor((void *)self, RegistryKey_dest); return self; error: talloc_free(self); return NULL; } /* XXX: would be nice to change this into a property, rather than a function, * but that would require a custom __getattr__. Can that be done? */ static SubkeyIterator RegistryKey_subkeys(RegistryKey self) { return CONSTRUCT(SubkeyIterator, SubkeyIterator, Con, NULL, self->file, self->key); } /* XXX: would be nice to change this into a property, rather than a function, * but that would require a custom __getattr__. Can that be done? */ static SubkeyIterator RegistryKey_values(RegistryKey self) { return CONSTRUCT(ValueIterator, ValueIterator, Con, NULL, self->file, self->key); } VIRTUAL(RegistryKey, Object) { VMETHOD(Con) = RegistryKey_Con; VMETHOD(subkeys) = RegistryKey_subkeys; VMETHOD(values) = RegistryKey_values; } END_VIRTUAL static int TreeIterator_dest(void *self) { TreeIterator this = (TreeIterator)self; regfi_iterator_free(this->iter); return 0; } static TreeIterator TreeIterator_Con(TreeIterator self, RegistryFile file, char **path, REGFI_ENCODING encoding) { self->iter = regfi_iterator_new(file->reg, encoding); self->file = file; if(!self->iter) { RaiseError(ERuntimeError, "Error: %s", regfi_log_get_str()); goto error; } talloc_set_destructor((void*)self, TreeIterator_dest); /* Traverse to the path */ if(path[0]) { if(!regfi_iterator_walk_path(self->iter, (const char **)path)) { RaiseError(ERuntimeError, "Unable to walk down key path"); goto error; } } self->root_traversed = false; return self; error: return NULL; } static void TreeIterator__iter__(TreeIterator self) { return; } static RegistryKey TreeIterator_next(TreeIterator self) { if(!self->root_traversed) self->root_traversed = true; else if(!regfi_iterator_down(self->iter)) { do { if(!regfi_iterator_up(self->iter)) return NULL; } while(!regfi_iterator_next_subkey(self->iter)); /* XXX: This is an error condition. * Probably should throw an exception or something. */ if(!regfi_iterator_down(self->iter)) return NULL; } regfi_iterator_first_subkey(self->iter); return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file, regfi_iterator_cur_key(self->iter)); } static int TreeIterator_down(TreeIterator self) { int result = regfi_iterator_down(self->iter); regfi_iterator_first_subkey(self->iter); regfi_iterator_first_value(self->iter); return result; } static int TreeIterator_up(TreeIterator self) { return regfi_iterator_up(self->iter); } static RegistryKey TreeIterator_current(TreeIterator self) { return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file, regfi_iterator_cur_key(self->iter)); } static int TreeIterator_to_root(TreeIterator self) { return regfi_iterator_to_root(self->iter); } VIRTUAL(TreeIterator, Object) { VMETHOD(Con) = TreeIterator_Con; VMETHOD(iternext) = TreeIterator_next; VMETHOD(down) = TreeIterator_down; VMETHOD(up) = TreeIterator_up; VMETHOD(current) = TreeIterator_current; VMETHOD(to_root) = TreeIterator_to_root; VMETHOD(__iter__) = TreeIterator__iter__; /* VMETHOD(list_values) = TreeIterator_list_values;*/ } END_VIRTUAL static int SubkeyIterator_dest(void *self) { SubkeyIterator this = (SubkeyIterator)self; talloc_unlink(this, (void*)this->list); return 0; } static SubkeyIterator SubkeyIterator_Con(SubkeyIterator self, struct RegistryFile_t* file, REGFI_NK* key) { /* XXX: add a talloc reference? */ self->file = file; /* Add a reference to the list */ self->list = key->subkeys; talloc_reference(self, self->list); self->cur = 0; talloc_set_destructor((void *)self, SubkeyIterator_dest); return self; } static void SubkeyIterator__iter__(SubkeyIterator self) { return; } static RegistryKey SubkeyIterator_iternext(SubkeyIterator self) { const REGFI_NK* nk; if(self->list != NULL && self->cur < self->list->num_keys) { /* XXX: can we switch to UTF-8 and have Python properly import that? */ nk = regfi_load_key(self->file->reg, self->list->elements[self->cur].offset+REGFI_REGF_SIZE, REGFI_ENCODING_ASCII, true); self->cur++; return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file, nk); } return NULL; } VIRTUAL(SubkeyIterator, Object) { VMETHOD(Con) = SubkeyIterator_Con; VMETHOD(__iter__) = SubkeyIterator__iter__; VMETHOD(iternext) = SubkeyIterator_iternext; } END_VIRTUAL static int ValueIterator_dest(void *self) { ValueIterator this = (ValueIterator)self; if(this->list != NULL) talloc_unlink(this, (void*)this->list); return 0; } static ValueIterator ValueIterator_Con(ValueIterator self, struct RegistryFile_t* file, REGFI_NK* key) { /* XXX: add a talloc reference? */ self->file = file; self->cur = 0; /* Add a reference to the list */ self->list = key->values; if(self->list != NULL) talloc_reference(self, self->list); talloc_set_destructor((void *)self, ValueIterator_dest); return self; } static void ValueIterator__iter__(ValueIterator self) { return; } static REGFI_VK* ValueIterator_iternext(ValueIterator self) { const REGFI_VK* vk; if(self->list != NULL && self->cur < self->list->num_values) { /* XXX: can we switch to UTF-8 and have Python properly import that? */ vk = regfi_load_value(self->file->reg, self->list->elements[self->cur]+REGFI_REGF_SIZE, REGFI_ENCODING_ASCII, true); self->cur++; /*return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, vk); */ return vk; } return NULL; /* XXX: shouldn't parse data here every time we walk over a value. * Instead, make data fetching a method and parse it then. */ /* data = (REGFI_DATA *)regfi_iterator_fetch_data(self->iter, rec); if(!data) { RaiseError(ERuntimeError, "Unable to fetch data: %s", regfi_log_get_str()); goto error; } switch(rec->type) { case REG_EXPAND_SZ: case REG_SZ: result = (RawData)CONSTRUCT(DataString, RawData, Con, NULL, data, rec); break; case REG_DWORD: result = (RawData)CONSTRUCT(DWORDData, RawData, Con, NULL, data, rec); break; case REG_BINARY: default: result = (RawData)CONSTRUCT(RawData, RawData, Con, NULL, data, rec); break; } return result; error: talloc_free(self); return NULL; */ } VIRTUAL(ValueIterator, Object) { VMETHOD(Con) = ValueIterator_Con; VMETHOD(__iter__) = ValueIterator__iter__; VMETHOD(iternext) = ValueIterator_iternext; } END_VIRTUAL static int RawData_dest(void *self) { RawData this = (RawData)self; if(this->data) { regfi_free_record(this->data); }; if(this->rec) { regfi_free_record(this->rec); }; return 0; } static RawData RawData_Con(RawData self, REGFI_DATA *data, REGFI_VK *record) { self->rec = record; self->data = data; talloc_set_destructor((void *)self, RawData_dest); return self; } static int RawData_get_value(RawData self, char *buff, int len) { int available_to_read = min(len, self->data->interpreted_size); memcpy(buff, self->data->raw, available_to_read); return available_to_read; } VIRTUAL(RawData, Object) { VMETHOD(Con) = RawData_Con; VMETHOD(get_value) = RawData_get_value; } END_VIRTUAL static char* DataString_get_value(DataString self) { RawData this = (RawData)self; return (char*)this->data->interpreted.string; } VIRTUAL(DataString, RawData) { VMETHOD(get_value) = DataString_get_value; } END_VIRTUAL static uint64_t DWORDData_get_value(DWORDData self) { RawData this = (RawData)self; return this->data->interpreted.dword; } VIRTUAL(DWORDData, RawData) { VMETHOD(get_value) = DWORDData_get_value; } END_VIRTUAL void pyregfi_init() { INIT_CLASS(RegistryFile); } reglookup+git/include/compat.h000664 001750 001750 00000002063 12566244653 017563 0ustar00sophiesophie000000 000000 /* * Copyright (C) 2010-2011 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 * 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: regfi.h 252 2011-05-08 17:33:49Z tim $ */ #ifndef _COMPAT_H #define _COMPAT_H /* GCC-specific macro for library exports */ #ifdef _EXPORT #undef _EXPORT #endif #ifdef REGFI_WIN32 #define _EXPORT() __declspec(dllexport) #else #define _EXPORT() __attribute__((visibility("default"))) #endif #ifndef EOVERFLOW # define EOVERFLOW E2BIG #endif #endif /*_COMPAT_H*/ reglookup+git/python/experimental/regfi/000775 001750 001750 00000000000 12566244653 021615 5ustar00sophiesophie000000 000000 reglookup+git/include/byteorder.h000664 001750 001750 00000016041 12566244653 020300 0ustar00sophiesophie000000 000000 /* * Branched from Samba project Subversion repository, version #2: * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h * * Unix SMB/CIFS implementation. * SMB Byte handling * * Copyright (C) 2005 Timothy D. Morgan * Copyright (C) 1992-1998 Andrew Tridgell * * 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$ */ #ifndef _BYTEORDER_H #define _BYTEORDER_H /** * @file * * This file implements macros for machine independent short and * int manipulation @verbatim Here is a description of this file that I emailed to the samba list once: > I am confused about the way that byteorder.h works in Samba. I have > looked at it, and I would have thought that you might make a distinction > between LE and BE machines, but you only seem to distinguish between 386 > and all other architectures. > > Can you give me a clue? sure. The distinction between 386 and other architectures is only there as an optimisation. You can take it out completely and it will make no difference. The routines (macros) in byteorder.h are totally byteorder independent. The 386 optimsation just takes advantage of the fact that the x86 processors don't care about alignment, so we don't have to align ints on int boundaries etc. If there are other processors out there that aren't alignment sensitive then you could also define 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_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) You do this: #define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) #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_t value at offset 25 in a buffer you do this: char *buffer = foo_bar(); 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 code, especially if it happens to have the right byteorder :-) I know these macros can be made a bit tidier by removing some of the casts, but you need to look at byteorder.h as a whole to see the reasoning behind them. byteorder.h defines the following macros: SVAL(buf,pos) - extract a 2 byte SMB value IVAL(buf,pos) - extract a 4 byte SMB value SVALS(buf,pos) signed version of SVAL() IVALS(buf,pos) signed version of IVAL() SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer SSVALS(buf,pos,val) - signed version of SSVAL() SIVALS(buf,pos,val) - signed version of SIVAL() RSVAL(buf,pos) - like SVAL() but for NMB byte ordering RSVALS(buf,pos) - like SVALS() but for NMB byte ordering RIVAL(buf,pos) - like IVAL() but for NMB byte ordering RIVALS(buf,pos) - like IVALS() but for NMB byte ordering RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering it also defines lots of intermediate macros, just ignore those :-) @endverbatim */ #undef CAREFUL_ALIGNMENT /* we know that the 386 can handle misalignment and has the "right" byteorder */ #ifdef __i386__ #define CAREFUL_ALIGNMENT 0 #endif #ifndef CAREFUL_ALIGNMENT #define CAREFUL_ALIGNMENT 1 #endif #define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos])) #define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */ #define PVAL(buf,pos) (CVAL(buf,pos)) #define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val)) #if CAREFUL_ALIGNMENT #define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) #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_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_t and int32_t being correct */ /* get single value from an SMB buffer */ #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_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 */ /* now the reverse routines - these are used in nmb packets (mostly) */ #define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) #define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16))) #define RSVAL(buf,pos) SREV(SVAL(buf,pos)) #define RSVALS(buf,pos) SREV(SVALS(buf,pos)) #define RIVAL(buf,pos) IREV(IVAL(buf,pos)) #define RIVALS(buf,pos) IREV(IVALS(buf,pos)) #define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val)) #define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val)) #define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val)) #define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val)) /* Alignment macros. */ #define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3)) #define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1)) #endif /* _BYTEORDER_H */ reglookup+git/lib/range_list.c000664 001750 001750 00000017375 12566244653 017561 0ustar00sophiesophie000000 000000 /* * 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 * 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$ */ /** * @file */ #include "range_list.h" /*******************/ /* Private symbols */ /*******************/ #define RANGE_LIST_ALLOC_SIZE 256 #if 0 /* For debugging */ #include static void range_list_print(const range_list* rl) { uint32_t i; for(i=0; isize; i++) fprintf(stderr, " %d=%p,%d,%d,%p", i, (void*)rl->elements[i], rl->elements[i]->offset, rl->elements[i]->length, rl->elements[i]->data); fprintf(stderr, "\n"); } #endif /* * Inserts elem into rl at the specified index and updates rl->size. * Memory reallocation of rl->elements is handled when necessary, and * rl->elem_alloced is updated in this case.. Returns false if memory * could not be allocated. */ static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t index) { uint32_t i; range_list_element** tmp; if(rl->size == rl->elem_alloced) { tmp = talloc_realloc(rl, rl->elements, range_list_element*, (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE)); if(tmp == NULL) return false; rl->elements = tmp; rl->elem_alloced += RANGE_LIST_ALLOC_SIZE; } /* Do the shuffle to the right. */ for(i=rl->size; i > index; i--) rl->elements[i] = rl->elements[i-1]; rl->elements[index] = elem; rl->size++; return true; } /* * Finds the element with the closest offset to that provided, such that * the element's offset <= the provided offset. If no such element * exists, this returns -1 which indicates that the provided offset * appears before all elements. */ static int32_t range_list_find_previous(const range_list* rl, uint32_t offset) { uint32_t h_idx, l_idx, cur_idx; uint32_t h_val, l_val; range_list_element* cur_elem; if((rl->size == 0) || (offset < rl->elements[0]->offset)) return -1; if(offset >= rl->elements[rl->size-1]->offset) return rl->size-1; h_idx = rl->size-1; l_idx = 0; while(h_idx != l_idx) { h_val = rl->elements[h_idx]->offset + rl->elements[h_idx]->length; l_val = rl->elements[l_idx]->offset; /* Make an educated guess as to the "middle" index based on the * ratios of the offset and high/low values. */ cur_idx = (uint32_t)ceil((((double)offset-l_val)/(h_val-l_val))*(h_idx-l_idx)); if(cur_idx > h_idx) cur_idx = h_idx; if(cur_idx < l_idx) cur_idx = l_idx; cur_elem = rl->elements[cur_idx]; if((offset >= cur_elem->offset) && (offset < rl->elements[cur_idx+1]->offset)) return cur_idx; if(offset < cur_elem->offset) h_idx = cur_idx-1; else l_idx = cur_idx+1; } return h_idx; } /******************/ /* Public symbols */ /******************/ range_list* range_list_new() { range_list* rl; rl = talloc(NULL, range_list); if(rl == NULL) return NULL; rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE); if(rl->elements == NULL) { talloc_free(rl); return NULL; } rl->elem_alloced = RANGE_LIST_ALLOC_SIZE; rl->size = 0; return rl; } void range_list_free(range_list* rl) { if(rl != NULL) talloc_free(rl); } uint32_t range_list_size(const range_list* rl) { return rl->size; } bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data) { uint32_t insert_index; range_list_element* elem; range_list_element* prev_elem; /*fprintf(stderr, "DEBUG: rl->size=%d\n", rl->size);*/ /* Sorry, limited to 2**31-1 elements. */ if(rl->size >= 0x7FFFFFFF) return false; /* 0-length ranges aren't allowed. */ if(length == 0) return false; /* Check for integer overflows */ if((uint32_t)(offset+length) < offset || (uint32_t)(offset+length) < length) return false; /* Find insertion point and validate there are no overlaps */ insert_index = range_list_find_previous(rl, offset)+1; /* Does the previous element overlap with this one? */ if(insert_index > 0) { prev_elem = rl->elements[insert_index-1]; if(offset < prev_elem->length + prev_elem->offset) return false; } /* Does this new element overlap with the next one? */ if((insert_index+1 < rl->size) && (offset+length > rl->elements[insert_index+1]->offset)) return false; elem = talloc(rl->elements, range_list_element); if(elem == NULL) return false; elem->offset = offset; elem->length = length; elem->data = data; if(!range_list_insert(rl, elem, insert_index)) { talloc_free(elem); return false; } return true; } bool range_list_remove(range_list* rl, uint32_t index) { uint32_t i; range_list_element** tmp; if(index >= rl->size) return false; talloc_free(rl->elements[index]); /* Do the shuffle to the left. */ for(i=index; i < (rl->size-1); i++) rl->elements[i] = rl->elements[i+1]; rl->elements[rl->size-1] = NULL; rl->size--; /* Try to keep memory usage down */ if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE < rl->elem_alloced) { tmp = talloc_realloc(rl, rl->elements, range_list_element*, (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE)); if(tmp != NULL) { rl->elements = tmp; rl->elem_alloced -= 2*RANGE_LIST_ALLOC_SIZE; } } return true; } const range_list_element* range_list_get(const range_list* rl, uint32_t index) { if(index >= rl->size) return NULL; return rl->elements[index]; } int32_t range_list_find(const range_list* rl, uint32_t offset) { uint32_t prev_idx; range_list_element* elem; if(rl->size == 0) return -1; if((offset < rl->elements[0]->offset) || (offset > rl->elements[rl->size-1]->offset + rl->elements[rl->size-1]->length)) return -2; prev_idx = range_list_find_previous(rl, offset); elem = rl->elements[prev_idx]; if(offset < elem->offset+elem->length) return prev_idx; return -3; } void* range_list_find_data(const range_list* rl, uint32_t offset) { int32_t index = range_list_find(rl, offset); if(index < 0) return NULL; return rl->elements[index]->data; } bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset) { range_list_element* cur_elem; range_list_element* new_elem; if(index >= rl->size) return false; cur_elem = rl->elements[index]; if((offset <= cur_elem->offset) || (offset >= cur_elem->offset+cur_elem->length)) return false; new_elem = talloc(rl->elements, range_list_element); if(new_elem == NULL) return false; new_elem->offset = offset; new_elem->length = cur_elem->offset + cur_elem->length - offset; new_elem->data = cur_elem->data; if(!range_list_insert(rl, new_elem, index+1)) { talloc_free(new_elem); return false; } cur_elem->length = new_elem->offset - cur_elem->offset; return true; } bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length) { int32_t idx1, idx2; idx1 = range_list_find(rl, start); if(idx1 < 0) return false; idx2 = range_list_find(rl, start+length); if(idx2 < 0) return false; if(idx1 == idx2) return true; while(idx1 != idx2) { if(rl->elements[idx1]->offset + rl->elements[idx1]->length != rl->elements[idx1+1]->offset) return false; idx1++; } return true; } reglookup+git/include/winsec.h000664 001750 001750 00000016023 12566244653 017571 0ustar00sophiesophie000000 000000 /* * Copyright (C) 2005,2009-2011 Timothy D. Morgan * Copyright (C) 1992-2005 Samba development team * * 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$ */ /** * @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 #define _WINSEC_H #include #include #include #include #include #include #include #include #include #include #include #include "compat.h" #include "byteorder.h" /* This is the maximum number of subauths in a SID, as defined here: * http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx */ #define WINSEC_MAX_SUBAUTHS 15 #define WINSEC_DESC_HEADER_SIZE (5 * sizeof(uint32_t)) #define WINSEC_ACL_HEADER_SIZE (2 * sizeof(uint32_t)) #define WINSEC_ACE_MIN_SIZE 16 /* XXX: Fill in definitions of other flags */ /* This self relative flag means offsets contained in the descriptor are relative * to the descriptor's offset. This had better be true in the registry. */ #define WINSEC_DESC_SELF_RELATIVE 0x8000 #define WINSEC_DESC_SACL_PRESENT 0x0010 #define WINSEC_DESC_DACL_PRESENT 0x0004 #define WINSEC_ACE_OBJECT_PRESENT 0x00000001 #define WINSEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002 #define WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT 0x5 #define WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT 0x6 #define WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT 0x7 #define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8 /** XXX: document this. */ typedef struct _winsec_uuid { /** 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 { /** 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. */ uint32_t sub_auths[WINSEC_MAX_SUBAUTHS]; /* XXX: Make this dynamically allocated? */ } WINSEC_DOM_SID; /** XXX: document this. */ typedef struct _winsec_ace { /** 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; /** Object GUID */ WINSEC_UUID* obj_guid; /** 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 { /** 0x0003 */ uint16_t revision; /** Size, in bytes, of the entire ACL structure */ uint16_t size; /** Number of Access Control Entries */ uint32_t num_aces; /** XXX: document this. */ WINSEC_ACE** aces; } WINSEC_ACL; /** XXX: document this. */ typedef struct _winsec_desc { /** 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 */ _EXPORT() WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ _EXPORT() void winsec_free_descriptor(WINSEC_DESC* desc); /** * * XXX: finish documenting */ _EXPORT() WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ _EXPORT() WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ _EXPORT() WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ _EXPORT() WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ _EXPORT() WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ _EXPORT() size_t winsec_sid_size(const WINSEC_DOM_SID* sid); /** * * XXX: finish documenting */ _EXPORT() int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2); /** * * XXX: finish documenting */ _EXPORT() int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2); /** * * XXX: finish documenting */ _EXPORT() bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2); /** * * XXX: finish documenting */ _EXPORT() char* winsec_sid2str(const WINSEC_DOM_SID* sid); /** * * XXX: finish documenting */ _EXPORT() bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2); /** * * XXX: finish documenting */ _EXPORT() bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2); /** * * XXX: finish documenting */ _EXPORT() bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2); /** * * XXX: finish documenting */ _EXPORT() bool winsec_ace_object(uint8_t type); #endif /* _WINSEC_H */ reglookup+git/include/regfi.h000664 001750 001750 00000144317 12566244653 017405 0ustar00sophiesophie000000 000000 /* * Copyright (C) 2005-2011 Timothy D. Morgan * Copyright (C) 2010 Michael Cohen * Copyright (C) 2005 Gerald (Jerry) Carter * * 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$ */ /** * @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 willing * 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. */ #ifndef _REGFI_H #define _REGFI_H #include #include #include #include #include #include #include #include #include #include #include #include #include /* regfi headers */ #include "compat.h" #include "byteorder.h" #include "winsec.h" #include "void_stack.h" #include "range_list.h" #include "lru_cache.h" /******************************************************************************/ /* Constants for use while interacting with the library */ /******************************************************************************/ /* regfi library error message types */ #define REGFI_LOG_INFO 0x0001 #define REGFI_LOG_WARN 0x0004 #define REGFI_LOG_ERROR 0x0010 #define REGFI_DEFAULT_LOG_MASK REGFI_LOG_ERROR|REGFI_LOG_WARN /* regfi library supported character encodings */ /* UTF16LE is not supported for output */ typedef enum { REGFI_ENCODING_DEFAULT = 0, REGFI_ENCODING_ASCII = 0, REGFI_ENCODING_UTF8 = 1, REGFI_ENCODING_UTF16LE = 2, REGFI_NUM_ENCODINGS = 3 } REGFI_ENCODING; /* Registry data types */ typedef enum { REG_NONE = 0, REG_SZ = 1, REG_EXPAND_SZ = 2, REG_BINARY = 3, REG_DWORD = 4, REG_DWORD_LE = 4 , /* DWORD, little endian */ REG_DWORD_BE = 5 , /* DWORD, big endian */ REG_LINK = 6, REG_MULTI_SZ = 7, REG_RESOURCE_LIST = 8, REG_FULL_RESOURCE_DESCRIPTOR= 9, REG_RESOURCE_REQUIREMENTS_LIST= 10, REG_QWORD = 11, /* 64-bit little endian */ /* XXX: Has MS defined a REG_QWORD_BE? */ /* Not a real type in the registry */ REG_KEY = 0x7FFFFFFF } REGFI_DATA_TYPE; #define REGFI_OFFSET_NONE 0xffffffff /******************************************************************************/ /* Various resource limits and related constants */ /******************************************************************************/ /* Flags determining how many records to cache internally */ #define REGFI_CACHE_SK_MAX 64 #define REGFI_CACHE_NK_MAX 1024 /* 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 /******************************************************************************/ /* Symbols for internal use */ /******************************************************************************/ /* Global thread-local storage key */ pthread_key_t regfi_log_key; /* 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 */ #define REGFI_REGF_MAGIC_SIZE 4 #define REGFI_REGF_NAME_SIZE 64 #define REGFI_REGF_RESERVED1_SIZE 340 #define REGFI_REGF_RESERVED2_SIZE 3528 #define REGFI_HBIN_MAGIC_SIZE 4 #define REGFI_CELL_MAGIC_SIZE 2 #define REGFI_HBIN_HEADER_SIZE 0x20 #define REGFI_NK_MIN_LENGTH 0x4C #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 */ /* XXX: Can we add clock resolution validation as well as range? It has * been reported that Windows timestamps are never more than a * certain granularity (250ms?), which could be used to help * eliminate false positives. Would need to verify this and * perhaps conservatively implement a check. */ /* Minimum time is Jan 1, 1990 00:00:00 */ #define REGFI_MTIME_MIN 0x01B41E6D00000000L /* Maximum time is Jan 1, 2290 00:00:00 * (We hope no one is using Windows by then...) */ #define REGFI_MTIME_MAX 0x0304754300000000L /* Flags for the vk records */ #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 */ /* Known key flags */ /*******************/ /* These next two show up on normal-seeming keys in Vista and W2K3 registries */ #define REGFI_NK_FLAG_UNKNOWN1 0x4000 #define REGFI_NK_FLAG_UNKNOWN2 0x1000 /* 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 * where the handle is stored. * http://msdn.microsoft.com/en-us/library/ms724836(VS.85).aspx */ #define REGFI_NK_FLAG_PREDEF_KEY 0x0040 /* The name will be in ASCII if this next bit is set, otherwise UTF-16LE */ #define REGFI_NK_FLAG_ASCIINAME 0x0020 /* Symlink key. * See: http://www.codeproject.com/KB/system/regsymlink.aspx */ #define REGFI_NK_FLAG_LINK 0x0010 /* This key cannot be deleted */ #define REGFI_NK_FLAG_NO_RM 0x0008 /* Root of a hive */ #define REGFI_NK_FLAG_ROOT 0x0004 /* Mount point of another hive. NULL/(default) value indicates which hive * and where in the hive it points to. */ #define REGFI_NK_FLAG_HIVE_LINK 0x0002 /* These keys shouldn't be stored on disk, according to: * http://geekswithblogs.net/sdorman/archive/2007/12/24/volatile-registry-keys.aspx */ #define REGFI_NK_FLAG_VOLATILE 0x0001 /* Useful for identifying unknown flag types */ #define REGFI_NK_KNOWN_FLAGS (REGFI_NK_FLAG_PREDEF_KEY\ | REGFI_NK_FLAG_ASCIINAME\ | REGFI_NK_FLAG_LINK\ | REGFI_NK_FLAG_NO_RM\ | REGFI_NK_FLAG_ROOT\ | REGFI_NK_FLAG_HIVE_LINK\ | REGFI_NK_FLAG_VOLATILE\ | REGFI_NK_FLAG_UNKNOWN1\ | REGFI_NK_FLAG_UNKNOWN2\ | REGFI_NK_FLAG_UNKNOWN3) #ifndef CHAR_BIT #define CHAR_BIT 8 #endif #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 REGFI_TIME_FIXUP (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60)) /******************************************************************************/ /* Structures */ /******************************************************************************/ typedef uint64_t REGFI_NTTIME; typedef struct _regfi_log { /* Error/warning/info messages returned by lower layer functions */ char* messages; /* Mask for error message types that will be stored. */ uint16_t msg_mask; } REGFI_LOG; /** HBIN block information * @ingroup regfiMiddleLayer */ typedef struct _regfi_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; /* Subkey List -- list of key offsets and hashed names for consistency */ typedef struct { /* Virtual offset of NK record or additional subkey list, * depending on this list's type. */ uint32_t offset; 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_t offset; uint32_t cell_size; /* Number of immediate children */ uint32_t num_children; /* Total number of keys referenced by this list and its children */ uint32_t num_keys; REGFI_SUBKEY_LIST_ELEM* elements; 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_t REGFI_VALUE_LIST_ELEM; /** Value-list structure * @ingroup regfiMiddleLayer */ typedef struct _regfi_value_list { /* Real offset of this record's cell in the file */ uint32_t offset; uint32_t cell_size; /* Actual number of values referenced by this list. * May differ from parent key's num_values if there were parsing errors. */ uint32_t num_values; REGFI_VALUE_LIST_ELEM* elements; } REGFI_VALUE_LIST; /** Class name structure (used in storing SysKeys) * @ingroup regfiBase */ typedef struct _regfi_classname { /** Real offset of this record's cell in the file */ uint32_t offset; /** 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; /** Data record structure * @ingroup regfiBase */ typedef struct _regfi_data { /* XXX: this isn't populated yet. Should set it to start of data cell * or big data cell. */ uint32_t offset; /** Data type of this data, as indicated by the referencing VK record. */ REGFI_DATA_TYPE 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. * Will be 0 if interpretation failed for any reason. */ 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 _regfi_vk { /** Real offset of this record's cell in the file */ uint32_t offset; /** ((start_offset - end_offset) & 0xfffffff8) */ uint32_t cell_size; /** The name of this value converted to desired REGFI_ENCODING. * * This conversion typically occurs automatically through REGFI_ITERATOR * settings. String is NUL terminated. */ char* name; /** The raw value name * * Length of the buffer is stored in name_length. */ uint8_t* name_raw; /** Length of name_raw */ uint16_t name_length; /** Offset from beginning of this hbin block */ uint32_t hbin_off; /** 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 */ REGFI_DATA_TYPE 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; /* XXX: deprecated */ REGFI_DATA* data; } REGFI_VK; /* Key Security */ struct _regfi_sk; /** Security structure * @ingroup regfiBase */ typedef struct _regfi_sk { /** 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; /** Offset of this record from beginning of this hbin block */ uint32_t hbin_off; /** 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; /** Key structure * @ingroup regfiBase */ typedef struct _regfi_nk { /** 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; /** 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; /** 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 name_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* name; /** The raw key name * * Length of the buffer is stored in name_length. */ uint8_t* name_raw; /** Virtual offset of parent key */ uint32_t parent_off; /** Virtual offset of classname key */ uint32_t classname_off; /* XXX: max subkey name * 2 */ uint32_t max_bytes_subkeyname; /* XXX: max subkey classname length (as if) */ uint32_t max_bytes_subkeyclassname; /* XXX: max value name * 2 */ uint32_t max_bytes_valuename; /* XXX: max value data size */ uint32_t max_bytes_value; /* XXX: Fields of unknown purpose */ uint32_t unknown1; uint32_t unknown2; uint32_t unknown3; uint32_t unk_index; /* nigel says run time index ? */ /** 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; typedef struct _regfi_raw_file { int64_t (* seek)(); /* (REGFI_RAW_FILE* self, uint64_t offset, int whence) */ ssize_t (* read)(); /* (REGFI_RAW_FILE* self, void* buf, size_t count) */ uint64_t cur_off; uint64_t size; void* state; } REGFI_RAW_FILE; /** 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_log_set_mask. * Messages may be retrieved using @ref regfi_log_get_str. * * @note If the message mask is set to record any messages, dependent code * must use @ref regfi_log_get_str periodically to clear the message * queue. Otherwise, this structure will grow in size over time as * messages queue up. * * @ingroup regfiBase */ typedef struct _regfi_file { /* Data parsed from file header */ /********************************/ uint8_t magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */ /* These sequence numbers should match if * the hive was properly synced to disk. */ uint32_t sequence1; uint32_t sequence2; 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_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_t cluster; /* XXX: Unverified. Set to 1 in all known hives */ /* Matches hive's base file name. Stored in UTF-16LE */ 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_t flags; /* XXX: Unverified. */ uint32_t guid_signature; /* XXX: Unverified. */ 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_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_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_t reserved2[REGFI_REGF_RESERVED2_SIZE]; /* Run-time information */ /************************/ /* For sanity checking (not part of the registry header) */ uint32_t file_length; /** The encoding that all strings are converted to during interpretation. */ REGFI_ENCODING string_encoding; /* Functions for accessing the file */ REGFI_RAW_FILE* cb; /* Mutex for all cb access. This is done to prevent one thread from moving * the file offset while another thread is in the middle of a multi-read * parsing transaction */ pthread_mutex_t cb_lock; /* Metadata about hbins */ range_list* hbins; /* Multiple read access allowed, write access is exclusive */ pthread_rwlock_t hbins_lock; /* Small number of SK records cached */ lru_cache* sk_cache; /* Need exclusive access for LRUs, since lookups make changes */ pthread_mutex_t sk_lock; /* Limited number of keys cached */ lru_cache* nk_cache; /* Need exclusive access for LRUs, since lookups make changes */ pthread_mutex_t nk_lock; /* Needed to protect various talloc calls */ pthread_mutex_t mem_lock; } REGFI_FILE; typedef struct _regfi_iter_position { /* key offset */ uint32_t offset; /* Index of the current subkey */ uint32_t cur_subkey; /* Index of the current value */ uint32_t cur_value; /* The number of subkeys of this key */ uint32_t num_subkeys; /* The number of values of this key */ uint32_t num_values; } REGFI_ITER_POSITION; /** 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; REGFI_ITER_POSITION* cur; } REGFI_ITERATOR; /** General purpose buffer with stored length * @ingroup regfiBottomLayer */ typedef struct _regfi_buffer { uint8_t* buf; uint32_t len; } REGFI_BUFFER; /******************************************************************************/ /** * @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. */ /******************************************************************************/ /** Returns the current regfi library version * * @return A string indicating the version. * * @ingroup regfiBase */ _EXPORT() const char* regfi_version(); /** 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. * * @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 reference to a newly allocated REGFI_FILE structure, if successful; * NULL on error. Use regfi_free to free the returned REGFI_FILE. * * @ingroup regfiBase */ _EXPORT() REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding); /** Parses file headers returned by supplied callback functions. * * This interface is useful if you have a registry hive in memory * or have some other reason to emulate a real file. * * @param file_cb A structure defining the callback functions needed to access the file. * * @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 reference to a newly allocated REGFI_FILE structure, if successful; * NULL on error. Use regfi_free to free the returned REGFI_FILE. * * @ingroup regfiBase */ _EXPORT() REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb, REGFI_ENCODING output_encoding); /** Frees a hive's data structures without closing the underlying file. * * @param file The registry structure to free. * * @ingroup regfiBase */ _EXPORT() void regfi_free(REGFI_FILE* file); /** Get errors, warnings, and/or verbose information relating to processing of * the given registry file. * * @return A newly allocated char* which must be free()d by the caller. * * @ingroup regfiBase */ _EXPORT() char* regfi_log_get_str(); /** Set the verbosity level of messages generated by the library for the * current thread. * * @param mask An integer representing the types of messages desired. * Acceptable values are created through bitwise ORs of * REGFI_LOG_* values. For instance, if only errors and * informational messages were desired (but not warnings), * then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO * By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN. * * @return true on success and false on failure. Failure occurs if * underlying pthread functions fail. errno is set in this case. * * Message masks are set in a thread-specific way. If one were to set a message * mask in one thread and then spawn a new thread, then the new thread will have * it's message mask reset to the default. This function may be called at any * time and will take effect immediately for the current thread. * * @note When a non-zero message mask is set, messages will * accumulate in memory without limit if they are not fetched using * @ref regfi_log_get_str and subsequently freed by the caller. It is * recommended that messsages be fetched after each regfi API call in * order to provide the most context. * * @ingroup regfiBase */ _EXPORT() bool regfi_log_set_mask(uint16_t mask); /** Fetches a hive's root key. * * @return Returns the root key or NULL on failure. Key must be freed using * @ref regfi_free_record. * * @ingroup regfiBase */ _EXPORT() const REGFI_NK* regfi_get_rootkey(REGFI_FILE* file); /** Frees a record previously returned by one of the API functions. * * @param file The file from which the record originated. * (This is needed for memory management reasons.) * * @param record Any of the following record types: REGFI_NK, REGFI_VK, * REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records. * * @note The "const" in the record data type is a bit misleading and is there just for * convenience. Since records returned previously must not be modified by users * of the API due to internal caching, these are returned as const, so this * function is const to make passing those records back easy. * * @ingroup regfiBase */ _EXPORT() void regfi_free_record(REGFI_FILE* file, const void* record); /** Increments reference count on record * * Adds an extra internal reference to specified record, making it necessary to * call regfi_free_record on it an additional time before it is freed. This is * useful in cases where multiple threads/structures need access to a shared record, * without requiring them to be in sync with when it is freed. * * @param file The file from which the record originated. * (This is needed for memory management reasons.) * * @param record Any of the following record types: REGFI_NK, REGFI_VK, * REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records. * * @return Updated record pointer on success, NULL otherwise * * @note Be sure to use the returned record for further access to the structure * instead of the previous version of the pointer. E.g.: * @code * myKey = (const REGFI_NK*)regfi_reference_record(myFile, myKey); * @endcode * * @ingroup regfiBase */ _EXPORT() const void* regfi_reference_record(REGFI_FILE* file, const void* record); /** Retrieves number of subkeys referenced by this key. * * Number of subkeyss in key structure and subkey list structure could differ, * so this provides a standard/sane way of determining the number. * * @param key the key whose number of subkeys is desired * * @return Returns the number of subkeys referenced by this key. * * @ingroup regfiBase */ _EXPORT() uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key); /** Retrieves number of values referenced by this key. * * Number of values in key structure and value list structure could differ, * so this provides a standard/sane way of determining the number. * * @param key the key whose number of values is desired * * @return Returns the number of values referenced by this key. * * @ingroup regfiBase */ _EXPORT() uint32_t regfi_fetch_num_values(const REGFI_NK* key); /** Retrieves classname for a given key. * * @param file the file from which key is derived * @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 @ref regfi_free_record. * * @ingroup regfiBase */ _EXPORT() const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file, const REGFI_NK* key); /** Returns the SK (security) record referenced by the supplied key. * * @param file the file from which key is derived * @param key the key whose SK record is desired * * @return A read-only SK structure, or NULL on failure. * * @ingroup regfiBase */ _EXPORT() const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key); /** Returns the next SK (security) record referenced by the supplied SK record * * @param file the file from which sk is derived * @param sk the SK record whose next sibling SK record is desired * * @return A read-only SK structure, or NULL on failure. * * @note * SK records are included in a circular, doubly-linked list. * To iterate over all SK records, be sure to check for the repetition of * the SK record you started with to determine when all have been traversed. * * @ingroup regfiBase */ _EXPORT() const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk); /** Returns the previous SK (security) record referenced by the supplied SK record * * @param file the file from which sk is derived * @param sk the SK record whose previous sibling SK record is desired * * @return A read-only SK structure, or NULL on failure. * * @note * SK records are included in a circular, doubly-linked list. * To iterate over all SK records, be sure to check for the repetition of * the SK record you started with to determine when all have been traversed. * * @ingroup regfiBase */ _EXPORT() const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk); /** Retrieves data for a given value. * * @param file the file from which value is derived * @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 @ref regfi_free_record. * * @ingroup regfiBase */ _EXPORT() const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file, const REGFI_VK* value); /** Locates a specific subkey of a given key. * * @param file the file from which key is derived * @param key the key whose subkey is desired * @param name name of the desired subkey (case-insensitive) * @param index a return value: the index of the desired subkey. * undefined on error * * @return true if the subkey is found, false if an error occurred or if the * specified name could not be found. If an error occurs, messages * will be written explaining the issue. (See regfi_log_get_str.) * * @ingroup regfiBase */ _EXPORT() bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, const char* name, uint32_t* index); /** Locates a specific value of a given key. * * @param file the file from which key is derived * @param key the key whose value is desired * @param name name of the desired value (case-insensitive) * @param index a return value: the index of the desired value. * undefined on error * * @return true if the value is found, false if an error occurred or if the * specified name could not be found. If an error occurs, messages * will be written explaining the issue. (See regfi_log_get_str.) * * @ingroup regfiBase */ _EXPORT() bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key, const char* name, uint32_t* index); /** Retrieves a specific subkey of a given key. * * @param file the file from which key is derived * @param key the key whose subkey is desired * @param index the index of the desired subkey * * @return the requested subkey or NULL on error. * * @ingroup regfiBase */ _EXPORT() const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, uint32_t index); /** Retrieves a specific value of a given key. * * @param file the file from which key is derived * @param key the key whose value is desired * @param index the index of the desired value * * @return the requested value or NULL on error. * * @ingroup regfiBase */ _EXPORT() const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, uint32_t index); /** Uses a key's parent_off reference to retrieve it's parent. * * @param file the file from which key is derived * @param key the key whose parent is desired * * @return the requested subkey or NULL on error. * * @ingroup regfiBase */ _EXPORT() const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key); /******************************************************************************/ /** * @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. * * @return A newly allocated REGFI_ITERATOR. * Must be free()d with regfi_iterator_free. * * @ingroup regfiIteratorLayer */ _EXPORT() REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file); /** 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 */ _EXPORT() 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 */ _EXPORT() 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 */ _EXPORT() 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 */ _EXPORT() bool regfi_iterator_to_root(REGFI_ITERATOR* i); /** 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 */ _EXPORT() bool regfi_iterator_descend(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. * Data structures must be freed with @ref regfi_free_record. * * @ingroup regfiIteratorLayer */ _EXPORT() const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i); /** Sets the internal subkey index to the first subkey referenced by the current * key. * * @param i the iterator * * @return True if the current key has any subkeys, false otherwise. * * @ingroup regfiIteratorLayer */ _EXPORT() bool 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 * @ref regfi_free_record. * * @ingroup regfiIteratorLayer */ _EXPORT() const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i); /** Increments the internal subkey index to the next key in the subkey-list. * * @param i the iterator * * @return True if another subkey should exist, false otherwise. * * @ingroup regfiIteratorLayer */ _EXPORT() bool regfi_iterator_next_subkey(REGFI_ITERATOR* i); /** Searches for a subkey with a given name under the current key. * * @param i the iterator * @param 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 */ _EXPORT() bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name); /** Sets the internal value index to the first value referenced by the current * key. * * @param i the iterator * * @return True if the current key has any values, false otherwise. * * @ingroup regfiIteratorLayer */ _EXPORT() bool 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 * @ref regfi_free_record. * * @ingroup regfiIteratorLayer */ _EXPORT() const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i); /** Increments the internal value index to the next value in the value-list. * * @param i the iterator * * @return True if another value should exist, false otherwise. * * @ingroup regfiIteratorLayer */ _EXPORT() bool regfi_iterator_next_value(REGFI_ITERATOR* i); /** Searches for a value with a given name under the current key. * * @param i the iterator * @param 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 */ _EXPORT() bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name); /** Returns the current key and all parent keys as a list of NK records * * @param i the iterator * * @return An array of NK record pointers terminated by a NULL pointer. * This array may be passed directly to regfi_free_record to free * the entire array. * * @note In order to use an element of the array independently from the * array (that is, to hold a pointer to an individual NK record while * freeing the remaining array), callers must first use * regfi_reference_record on the elements to be kept. * * @ingroup regfiIteratorLayer */ _EXPORT() const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i); /******************************************************************************/ /** * @defgroup regfiGlueLayer Glue Layer: Logical Data Structure Loading */ /******************************************************************************/ /** Loads a key and associated data structures given a file offset. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ _EXPORT() REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, bool strict); /** Loads a value at a given file offset alng with associated data structures. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ _EXPORT() REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, bool strict); /** Loads a logical subkey list in its entirety which may span multiple records. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ _EXPORT() REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t num_keys, uint32_t max_size, bool strict); /** Loads a valuelist. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ _EXPORT() 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 */ _EXPORT() 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 */ _EXPORT() 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 */ _EXPORT() bool regfi_interpret_data(REGFI_FILE* file, uint32_t type, REGFI_DATA* data); /* These are cached so return values don't need to be freed. */ /** Loads an "sk" security record at the specified offset. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ _EXPORT() const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict); /** Retrieves the HBIN data structure stored at the specified offset. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ _EXPORT() const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset); /******************************************************************************/ /** * @defgroup regfiParseLayer Parsing Layer: Direct Data Structure Access */ /******************************************************************************/ _EXPORT() REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict); _EXPORT() REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict); /** 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 */ _EXPORT() REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Parses a single cell containing a subkey-list record. * * XXX: finish documenting * * @ingroup regfiParseLayer */ _EXPORT() REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Parses a VK (value) record at the specified offset * * XXX: finish documenting * * @ingroup regfiParseLayer */ _EXPORT() REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Parses an SK (security) record at the specified offset * * XXX: finish documenting * * @ingroup regfiParseLayer */ _EXPORT() REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t 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 */ _EXPORT() range_list* regfi_parse_unalloc_cells(REGFI_FILE* file); /** Helper function to parse a cell * * XXX: finish documenting * * @ingroup regfiParseLayer */ _EXPORT() bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, 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 */ _EXPORT() 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 */ _EXPORT() REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset, uint32_t length, bool strict); /** Parses a "little data" record which is stored entirely within the * provided virtual offset. * * XXX: finish documenting * * @ingroup regfiParseLayer */ _EXPORT() REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, uint32_t length, bool strict); /******************************************************************************/ /* Private (and undocumented) Functions */ /******************************************************************************/ int64_t regfi_raw_seek(REGFI_RAW_FILE* self, uint64_t offset, int whence); ssize_t regfi_raw_read(REGFI_RAW_FILE* self, void* buf, size_t count); _EXPORT() uint64_t regfi_seek(REGFI_RAW_FILE* file_cb, uint64_t offset, int whence); _EXPORT() uint32_t regfi_read(REGFI_RAW_FILE* file_cb, uint8_t* buf, uint32_t* length); _EXPORT() const char* regfi_type_val2str(unsigned int val); _EXPORT() int regfi_type_str2val(const char* str); _EXPORT() char* regfi_get_sacl(WINSEC_DESC* sec_desc); _EXPORT() char* regfi_get_dacl(WINSEC_DESC* sec_desc); _EXPORT() char* regfi_get_owner(WINSEC_DESC* sec_desc); _EXPORT() char* regfi_get_group(WINSEC_DESC* sec_desc); 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_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* regfi_copy_nk(const REGFI_NK* nk); REGFI_VK* regfi_copy_vk(const REGFI_VK* vk); _EXPORT() int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset); REGFI_BUFFER regfi_conv_charset(const char* input_charset, const char* output_charset, uint8_t* input, uint32_t input_len); _EXPORT() REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data); /* XXX: move to base API and document */ _EXPORT() REGFI_NTTIME regfi_unix2nt_time(time_t t); _EXPORT() double regfi_nt2unix_time(REGFI_NTTIME nt); _EXPORT() void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict); _EXPORT() void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict); _EXPORT() void regfi_init(); #endif /* _REGFI_H */ reglookup+git/src/common.c000664 001750 001750 00000022521 12566244653 016730 0ustar00sophiesophie000000 000000 /* * This file stores code common to the command line tools. * XXX: This should be converted to a proper library. * * Copyright (C) 2005-2008,2011 Timothy D. Morgan * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com * * 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$ */ #include iconv_t conv_desc; const char* key_special_chars = ",\"/"; const char* subfield_special_chars = ",\"|"; const char* common_special_chars = ",\""; #define REGLOOKUP_EXIT_OK 0 #define REGLOOKUP_EXIT_OSERR 71 #define REGLOOKUP_EXIT_USAGE 64 #define REGLOOKUP_EXIT_DATAERR 65 #define REGLOOKUP_EXIT_NOINPUT 66 /* Windows is lame */ #ifdef O_BINARY #define REGLOOKUP_OPEN_FLAGS O_RDONLY|O_BINARY #else #define REGLOOKUP_OPEN_FLAGS O_RDONLY #endif void bailOut(int code, char* message) { fprintf(stderr, "%s", message); exit(code); } void printMsgs() { char* msgs = regfi_log_get_str(); if(msgs != NULL) { fprintf(stderr, "%s", msgs); free(msgs); } } void clearMsgs() { char* msgs = regfi_log_get_str(); if(msgs != NULL) free(msgs); } /* Returns a newly malloc()ed string which contains original buffer, * except for non-printable or special characters are quoted in hex * with the syntax '%QQ' where QQ is the hex ascii value of the quoted * character. A null terminator is added, since only ascii, not binary, * is returned. */ static char* quote_buffer(const unsigned char* str, unsigned int len, const char* special) { unsigned int i, added_len; unsigned int num_written = 0; unsigned int buf_len = sizeof(char)*(len+1); char* ret_val = NULL; char* tmp_buf; if(buf_len > 0) ret_val = malloc(buf_len); if(ret_val == NULL) return NULL; for(i=0; i (len*4+1)) buf_len = len*4+1; else { if (added_len < 5) buf_len += 5; else buf_len += added_len; } tmp_buf = realloc(ret_val, buf_len); if(tmp_buf == NULL) { free(ret_val); return NULL; } ret_val = tmp_buf; } if(str[i] < 32 || str[i] > 126 || str[i] == '%' || strchr(special, str[i]) != NULL) { num_written += snprintf(ret_val + num_written, buf_len - num_written, "%%%.2X", str[i]); } else ret_val[num_written++] = str[i]; } ret_val[num_written] = '\0'; return ret_val; } /* Returns a newly malloc()ed string which contains original string, * except for non-printable or special characters are quoted in hex * with the syntax '%QQ' where QQ is the hex ascii value of the quoted * character. */ static char* quote_string(const char* str, const char* special) { unsigned int len; if(str == NULL) return NULL; len = strlen(str); return quote_buffer((const unsigned char*)str, len, special); } /* * 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 * be set to a newly allocated string, containing an error message. If * a memory allocation failure occurs while generating the error * message, both the return value and (*error_msg) will be NULL. It * is the responsibility of the caller to free both a non-NULL return * value, and a non-NULL (*error_msg). */ static char* data_to_ascii(const REGFI_DATA* data, char** error_msg) { char* ret_val; char* cur_quoted; char* tmp_ptr; char* delim; uint32_t ret_val_left, i, tmp_len; if(data == NULL || data->size == 0) { *error_msg = (char*)malloc(37); if(*error_msg == NULL) return NULL; strcpy(*error_msg, "Data pointer was NULL or size was 0."); return NULL; } *error_msg = NULL; 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: 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: 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."); return ret_val; break; case REG_DWORD: ret_val = malloc(sizeof(char)*(8+2+1)); if(ret_val == NULL) return NULL; sprintf(ret_val, "0x%.8X", data->interpreted.dword); return ret_val; break; case REG_DWORD_BE: ret_val = malloc(sizeof(char)*(8+2+1)); if(ret_val == NULL) return NULL; sprintf(ret_val, "0x%.8X", data->interpreted.dword_be); return ret_val; break; case REG_QWORD: ret_val = malloc(sizeof(char)*(16+2+1)); if(ret_val == NULL) return NULL; sprintf(ret_val, "0x%.16llX", (long long unsigned int)data->interpreted.qword); return ret_val; break; case REG_MULTI_SZ: ret_val_left = data->interpreted_size*4+1; ret_val = malloc(ret_val_left); if(ret_val == NULL) return NULL; tmp_ptr = ret_val; tmp_ptr[0] = '\0'; delim = ""; for(i=0; data->interpreted.multiple_string[i] != NULL; i++) { cur_quoted = quote_string((char*)data->interpreted.multiple_string[i], subfield_special_chars); if(cur_quoted != NULL) { 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 = "|"; } return ret_val; break; 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(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* nk) { char* ret_val; if(nk->name == NULL) ret_val = quote_buffer(nk->name_raw, nk->name_length, key_special_chars); else ret_val = quote_string(nk->name, key_special_chars); return ret_val; } static char* get_quoted_valuename(const REGFI_VK* vk) { char* ret_val; if(vk->name == NULL) ret_val = quote_buffer(vk->name_raw, vk->name_length, key_special_chars); else ret_val = quote_string(vk->name, key_special_chars); return ret_val; } int openHive(const char* filename) { int ret_val; /* open an existing file */ if ((ret_val = open(filename, REGLOOKUP_OPEN_FLAGS)) == -1) { fprintf(stderr, "ERROR: Failed to open hive. Error returned: %s\n", strerror(errno)); return -1; } return ret_val; } void formatTime(REGFI_NTTIME nttime, char* output) { time_t tmp_time[1]; struct tm* tmp_time_s = NULL; *tmp_time = (time_t)regfi_nt2unix_time(nttime); tmp_time_s = gmtime(tmp_time); strftime(output, (4+1+2+1+2)+1+(2+1+2+1+2)+1, "%Y-%m-%d %H:%M:%S", tmp_time_s); } reglookup+git/python/experimental/regfi/error.c000664 001750 001750 00000003523 12566244653 023115 0ustar00sophiesophie000000 000000 /* ** error.c ** ** Made by (mic) ** Login ** ** Started on Mon Mar 15 20:45:09 2010 mic ** Last update Sun May 12 01:17:25 2002 Speed Blue */ #include #include #include #include #include #include "aff4_errors.h" /** These slots carry the TLS error keys */ static pthread_key_t error_str_slot; static pthread_key_t error_value_slot; void error_dest(void *slot) { if(slot) talloc_free(slot); } void *raise_errors(enum _error_type t, char *reason, ...) { char *error_buffer; // This has to succeed: enum _error_type *type = aff4_get_current_error(&error_buffer); if(reason) { va_list ap; va_start(ap, reason); vsnprintf(error_buffer, ERROR_BUFFER_SIZE-1, reason,ap); error_buffer[ERROR_BUFFER_SIZE-1]=0; va_end(ap); }; //update the error type *type = t; return NULL; } static int error_subsystem_initialised=0; enum _error_type *aff4_get_current_error(char **error_buffer) { enum _error_type *type; if(!error_subsystem_initialised) error_init(); type = pthread_getspecific(error_value_slot); // This is optional if(error_buffer) { *error_buffer = pthread_getspecific(error_str_slot); // If TLS buffers are not set we need to create them if(!*error_buffer) { *error_buffer =talloc_size(NULL, ERROR_BUFFER_SIZE); pthread_setspecific(error_str_slot, *error_buffer); }; }; if(!type) { type = talloc(NULL, enum _error_type); pthread_setspecific(error_value_slot, type); }; return type; } /** Initialise the error subsystem */ void error_init() { error_subsystem_initialised = 1; // We create the error buffer slots if(pthread_key_create(&error_str_slot, error_dest) || pthread_key_create(&error_value_slot, error_dest)) { printf("Unable to set up TLS variables\n"); abort(); }; } reglookup+git/python/experimental/config.py000664 001750 001750 00000000056 12566244653 022341 0ustar00sophiesophie000000 000000 MINGW_XCOMPILE = False V = True DEBUG = True reglookup+git/lib/lru_cache.c000664 001750 001750 00000017657 12566244653 017362 0ustar00sophiesophie000000 000000 /* * Copyright (C) 2008-2009 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 * 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$ */ /** * @file */ #include "lru_cache.h" #define LRU_CACHE_DEBUG 0 /* XXX: really should replace this with a real universal hash or other * fast HMAC. */ static uint32_t lru_cache_compute_hash(uint32_t num_buckets, uint32_t secret, const void* buf, uint32_t buf_len) { uint32_t i; uint32_t ret_val = 0x243f6a88; unsigned char* s = (unsigned char*)&secret; const unsigned char* b = (unsigned char*)buf; for(i=0; i 1; ret_val--) if((n & (1 << ret_val)) != 0) return ret_val; return 0; } #if 0 _EXPORT() void lru_cache_print(lru_cache* ht) { uint32_t i; lru_cache_element* cur; printf("from newest to oldest:\n"); for(cur=ht->newest; cur != NULL; cur=cur->older) { /* write(STDOUT_FILENO, cur->index, cur->index_len);*/ printf("%p", (void*)cur); printf("\n"); if(cur->older == ht->newest) { printf("??? Loop in LRU list!!"); break; } } printf("\n"); printf("table:\n"); for(i=0; inum_buckets; i++) { printf("%.8X: ", i); for(cur=ht->table[i]; cur != NULL; cur=cur->next) { /* write(STDOUT_FILENO, cur->index, cur->index_len);*/ printf("%p", (void*)cur); printf("|"); if(cur->next == ht->table[i]) { printf("??? Loop in table chain!!"); break; } } printf("\n"); } } #endif lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret) { return lru_cache_create_ctx(NULL, max_keys, secret, false); } lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, uint32_t secret, bool talloc_data) { lru_cache* ret_val; ret_val = talloc(talloc_ctx, lru_cache); if(ret_val == NULL) return NULL; if(max_keys == 0) ret_val->num_buckets = 1024; else if(max_keys == 1) ret_val->num_buckets = 1; else { ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys); if(ret_val->num_buckets < 1) ret_val->num_buckets = 1; } ret_val->table = talloc_array(ret_val, lru_cache_element*, ret_val->num_buckets); if(ret_val->table == NULL) { talloc_free(ret_val); return NULL; } ret_val->oldest = NULL; ret_val->newest = NULL; ret_val->max_keys = max_keys; ret_val->secret = secret; ret_val->talloc_data = talloc_data; ret_val->num_keys = 0; memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*)); return ret_val; } void lru_cache_destroy(lru_cache* ht) { ht->secret = 0; talloc_unlink(NULL, ht); } bool lru_cache_update(lru_cache* ht, const void* index, uint32_t index_len, void* data) { uint32_t hash, lru_hash; lru_cache_element* cur; lru_cache_element* last = NULL; lru_cache_element* e = NULL; void* tmp_index; hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len); for(cur = ht->table[hash]; cur != NULL && e == NULL; cur=cur->next) { if((index_len == cur->index_len) && memcmp(cur->index, index, index_len) == 0) { e = cur; } } if(e != NULL) { /* We found the index, so we're going to overwrite the data. * We also need to reposition the element to the newest position, * so remove it from the list for now. */ if(ht->talloc_data) talloc_unlink(e, e->data); if(e->newer == NULL) ht->newest = e->older; else e->newer->older = e->older; if(e->older == NULL) ht->oldest = e->newer; else e->older->newer = e->newer; } else { /* We didn't find an identical index. */ if((ht->max_keys != 0) && (ht->num_keys >= ht->max_keys)) { /* Eliminate the least recently used item, but reuse the element * structure to minimize reallocation. */ e = ht->oldest; if(ht->newest == ht->oldest) { ht->newest = NULL; ht->oldest = NULL; } else { ht->oldest = e->newer; e->newer->older = NULL; } e->newer = NULL; e->older = NULL; last = NULL; lru_hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, e->index, e->index_len); for(cur = ht->table[lru_hash]; cur != e && cur != NULL; last=cur, cur=cur->next) { continue; } if(last == NULL) ht->table[lru_hash] = e->next; else last->next = e->next; e->next = NULL; if(ht->talloc_data) talloc_unlink(e, e->data); tmp_index = talloc_realloc_size(e, e->index, index_len); if(tmp_index == NULL) { talloc_free(e); return false; } else e->index = tmp_index; } else { /* Brand new element because we have room to spare. */ e = talloc(ht->table, lru_cache_element); if(e == NULL) return false; e->index = talloc_size(e, index_len); if(e->index == NULL) { talloc_free(e); return false; } /* New entry, increment counters. */ ht->num_keys++; } memcpy(e->index, index, index_len); e->index_len = index_len; e->next = ht->table[hash]; ht->table[hash] = e; } if(ht->talloc_data) data = talloc_reference(e, data); e->data = data; /* Finally, let's insert the element to the newest position in the LRU list.*/ if(ht->newest != NULL) ht->newest->newer = e; e->newer = NULL; e->older = ht->newest; ht->newest = e; if(ht->oldest == NULL) ht->oldest = e; return true; } void* lru_cache_find(lru_cache* ht, const void* index, uint32_t index_len) { uint32_t hash; lru_cache_element* cur; hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len); for(cur = ht->table[hash]; (cur != NULL); cur = cur->next) { if((index_len == cur->index_len) && memcmp(cur->index, index, index_len) == 0) { break; } } if(cur != NULL && cur->newer != NULL) { /* Need to move this element up to the newest slot. */ cur->newer->older = cur->older; if(cur->older == NULL) ht->oldest = cur->newer; else cur->older->newer = cur->newer; cur->newer = NULL; cur->older = ht->newest; ht->newest->newer = cur; ht->newest = cur; } if(cur != NULL) return cur->data; else return NULL; } bool lru_cache_remove(lru_cache* ht, const void* index, uint32_t index_len) { uint32_t hash; lru_cache_element* cur; lru_cache_element* last = NULL; hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len); for(cur=ht->table[hash]; (cur != NULL); last=cur, cur=cur->next) { if((index_len == cur->index_len) && memcmp(cur->index, index, index_len) == 0) { break; } } if(cur == NULL) return false; /* Detach from list */ if(cur->newer == NULL) ht->newest = cur->older; else cur->newer->older = cur->older; if(cur->older == NULL) ht->oldest = cur->newer; else cur->older->newer = cur->newer; /* Detach from hash table */ if(last == NULL) ht->table[hash] = cur->next; else last->next = cur->next; talloc_free(cur); /* Removing entry, decrement counters. */ ht->num_keys--; return true; } reglookup+git/python/pyregfi/structures.py000664 001750 001750 00000031227 12566244653 022273 0ustar00sophiesophie000000 000000 #!/usr/bin/env python # Copyright (C) 2010-2011 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 # 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: $ ## @package pyregfi.structures # Low-level data structures and C API mappings. # # Most users need not venture here. For more information, see the source. import sys import os import traceback import ctypes import ctypes.util from ctypes import * is_win32 = hasattr(ctypes, 'windll') # XXX: can we always be sure enums are this size? REGFI_ENCODING = c_uint32 REGFI_ENCODING_UTF8 = REGFI_ENCODING(1) REGFI_NK_FLAG_ASCIINAME = 0x0020 REGFI_VK_FLAG_ASCIINAME = 0x0001 REGFI_DATA_TYPE = c_uint32 REGFI_NTTIME = c_uint64 REGFI_REGF_SIZE = 0x1000 # Prototype everything first so we don't have to worry about reference order class REGFI_VK(Structure): pass class REGFI_SK(Structure): pass class REGFI_SUBKEY_LIST(Structure): pass class REGFI_VALUE_LIST(Structure): pass class REGFI_CLASSNAME(Structure): pass class REGFI_DATA(Structure): pass class REGFI_NK(Structure): pass class REGFI_ITERATOR(Structure): pass class REGFI_FILE(Structure): pass class REGFI_RAW_FILE(Structure): fh = None def cb_seek(self, raw_file, offset, whence): try: self.fh.seek(offset, whence) except Exception: traceback.print_exc() set_errno(74) # os.EX_IOERR return -1 return self.fh.tell() def cb_read(self, raw_file, buf, count): try: # XXX: anyway to do a readinto() here? tmp = self.fh.read(count) memmove(buf,tmp,len(tmp)) except Exception: traceback.print_exc() set_errno(74) # os.EX_IOERR return -1 return len(tmp) # Load libregfi according to platform regfi = None if is_win32: # XXX: Using C calling conventions on cross-compiled DLLs seems to work fine # on Windows, but I'm not sure if stdcall symbols are supported # correctly for native Windows binaries... #regfi = ctypes.windll.libregfi #CB_FACTORY = ctypes.WINFUNCTYPE regfi = ctypes.CDLL('libregfi.dll', use_errno=True) CB_FACTORY = ctypes.CFUNCTYPE else: regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True) CB_FACTORY = ctypes.CFUNCTYPE seek_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), c_uint64, c_int, use_errno=True) read_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), POINTER(c_char), c_size_t, use_errno=True) from .winsec import * REGFI_VK._fields_ = [('offset', c_uint32), ('cell_size', c_uint32), ('name', c_char_p), ('name_raw', POINTER(c_char)), ('name_length', c_uint16), ('hbin_off', c_uint32), ('data_size', c_uint32), ('data_off', c_uint32), ('type', REGFI_DATA_TYPE), ('magic', c_char * 2), ('flags', c_uint16), ('unknown1', c_uint16), ('data_in_offset', c_bool), ] REGFI_SK._fields_ = [('offset', c_uint32), ('cell_size', c_uint32), ('sec_desc', POINTER(WINSEC_DESC)), ('hbin_off', c_uint32), ('prev_sk_off', c_uint32), ('next_sk_off', c_uint32), ('ref_count', c_uint32), ('desc_size', c_uint32), ('unknown_tag', c_uint16), ('magic', c_char * 2), ] REGFI_NK._fields_ = [('offset', c_uint32), ('cell_size', c_uint32), ('values', POINTER(REGFI_VALUE_LIST)), ('subkeys', POINTER(REGFI_SUBKEY_LIST)), ('flags', c_uint16), ('magic', c_char * 2), ('mtime', REGFI_NTTIME), ('name_length', c_uint16), ('classname_length', c_uint16), ('name', c_char_p), ('name_raw', POINTER(c_char)), ('parent_off', c_uint32), ('classname_off', c_uint32), ('max_bytes_subkeyname', c_uint32), ('max_bytes_subkeyclassname', c_uint32), ('max_bytes_valuename', c_uint32), ('max_bytes_value', c_uint32), ('unknown1', c_uint32), ('unknown2', c_uint32), ('unknown3', c_uint32), ('unk_index', c_uint32), ('num_subkeys', c_uint32), ('subkeys_off', c_uint32), ('num_values', c_uint32), ('values_off', c_uint32), ('sk_off', c_uint32), ] REGFI_SUBKEY_LIST._fields_ = [('offset', c_uint32), ('cell_size', c_uint32), ('num_children', c_uint32), ('num_keys', c_uint32), ('elements', c_void_p), ('magic', c_char * 2), ('recursive_type', c_bool), ] REGFI_VALUE_LIST._fields_ = [('offset', c_uint32), ('cell_size', c_uint32), ('num_values', c_uint32), ('elements', c_void_p), ] REGFI_CLASSNAME._fields_ = [('offset', c_uint32), ('interpreted', c_char_p), ('raw', POINTER(c_char)), ('size', c_uint16), ] class REGFI_DATA__interpreted(Union): _fields_ = [('none',POINTER(c_char)), ('string', c_char_p), ('expand_string', c_char_p), ('binary',POINTER(c_char)), ('dword', c_uint32), ('dword_be', c_uint32), ('link', c_char_p), ('multiple_string', POINTER(c_char_p)), ('qword', c_uint64), ('resource_list',POINTER(c_char)), ('full_resource_descriptor',POINTER(c_char)), ('resource_requirements_list',POINTER(c_char)), ] REGFI_DATA._fields_ = [('offset', c_uint32), ('type', REGFI_DATA_TYPE), ('size', c_uint32), ('raw', POINTER(c_char)), ('interpreted_size', c_uint32), ('interpreted', REGFI_DATA__interpreted), ] REGFI_FILE._fields_ = [('magic', c_char * 4), ('sequence1', c_uint32), ('sequence2', c_uint32), ('mtime', REGFI_NTTIME), ('major_version', c_uint32), ('minor_version', c_uint32), ('type', c_uint32), ('format', c_uint32), ('root_cell', c_uint32), ('last_block', c_uint32), ('cluster', c_uint32), ] REGFI_RAW_FILE._fields_ = [('seek', seek_cb_type), ('read', read_cb_type), ('cur_off', c_uint64), ('size', c_uint64), ('state', c_void_p), ] # Define function prototypes regfi.regfi_version.argtypes = [] regfi.regfi_version.restype = c_char_p regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING] regfi.regfi_alloc.restype = POINTER(REGFI_FILE) regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING] regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE) regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)] regfi.regfi_free.restype = None regfi.regfi_log_get_str.argtypes = [] regfi.regfi_log_get_str.restype = c_char_p regfi.regfi_log_set_mask.argtypes = [c_uint16] regfi.regfi_log_set_mask.restype = c_bool regfi.regfi_get_rootkey.argtypes = [POINTER(REGFI_FILE)] regfi.regfi_get_rootkey.restype = POINTER(REGFI_NK) regfi.regfi_free_record.argtypes = [POINTER(REGFI_FILE), c_void_p] regfi.regfi_free_record.restype = None regfi.regfi_reference_record.argtypes = [POINTER(REGFI_FILE), c_void_p] regfi.regfi_reference_record.restype = c_void_p regfi.regfi_fetch_num_subkeys.argtypes = [POINTER(REGFI_NK)] regfi.regfi_fetch_num_subkeys.restype = c_uint32 regfi.regfi_fetch_num_values.argtypes = [POINTER(REGFI_NK)] regfi.regfi_fetch_num_values.restype = c_uint32 regfi.regfi_fetch_classname.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)] regfi.regfi_fetch_classname.restype = POINTER(REGFI_CLASSNAME) regfi.regfi_fetch_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)] regfi.regfi_fetch_sk.restype = POINTER(REGFI_SK) regfi.regfi_next_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)] regfi.regfi_next_sk.restype = POINTER(REGFI_SK) regfi.regfi_prev_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)] regfi.regfi_prev_sk.restype = POINTER(REGFI_SK) regfi.regfi_fetch_data.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_VK)] regfi.regfi_fetch_data.restype = POINTER(REGFI_DATA) regfi.regfi_find_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK), c_char_p, POINTER(c_uint32)] regfi.regfi_find_subkey.restype = c_bool regfi.regfi_find_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK), c_char_p, POINTER(c_uint32)] regfi.regfi_find_value.restype = c_bool regfi.regfi_get_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK), c_uint32] regfi.regfi_get_subkey.restype = POINTER(REGFI_NK) regfi.regfi_get_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK), c_uint32] regfi.regfi_get_value.restype = POINTER(REGFI_VK) regfi.regfi_get_parentkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)] regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK) regfi.regfi_nt2unix_time.argtypes = [REGFI_NTTIME] regfi.regfi_nt2unix_time.restype = c_double regfi.regfi_iterator_new.argtypes = [POINTER(REGFI_FILE)] regfi.regfi_iterator_new.restype = POINTER(REGFI_ITERATOR) regfi.regfi_iterator_free.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_free.restype = None regfi.regfi_iterator_down.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_down.restype = c_bool regfi.regfi_iterator_up.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_up.restype = c_bool regfi.regfi_iterator_to_root.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_to_root.restype = c_bool regfi.regfi_iterator_descend.argtypes = [POINTER(REGFI_ITERATOR), POINTER(c_char_p)] regfi.regfi_iterator_descend.restype = c_bool regfi.regfi_iterator_cur_key.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_cur_key.restype = POINTER(REGFI_NK) regfi.regfi_iterator_first_subkey.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_first_subkey.restype = c_bool regfi.regfi_iterator_cur_subkey.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_cur_subkey.restype = POINTER(REGFI_NK) regfi.regfi_iterator_next_subkey.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_next_subkey.restype = c_bool regfi.regfi_iterator_find_subkey.argtypes = [POINTER(REGFI_ITERATOR), c_char_p] regfi.regfi_iterator_find_subkey.restype = c_bool regfi.regfi_iterator_first_value.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_first_value.restype = c_bool regfi.regfi_iterator_cur_value.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_cur_value.restype = POINTER(REGFI_VK) regfi.regfi_iterator_next_value.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_next_value.restype = c_bool regfi.regfi_iterator_find_value.argtypes = [POINTER(REGFI_ITERATOR), c_char_p] regfi.regfi_iterator_find_value.restype = c_bool regfi.regfi_iterator_ancestry.argtypes = [POINTER(REGFI_ITERATOR)] regfi.regfi_iterator_ancestry.restype = POINTER(POINTER(REGFI_NK)) regfi.regfi_init.argtypes = [] regfi.regfi_init.restype = None regfi.regfi_init() reglookup+git/lib/void_stack.c000664 001750 001750 00000006463 12566244653 017554 0ustar00sophiesophie000000 000000 /* * 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 * 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$ */ /** * @file */ #include "void_stack.h" void_stack* void_stack_new(unsigned short max_size) { void_stack* ret_val = talloc(NULL, void_stack); if (ret_val != NULL) { memset(ret_val, 0, sizeof(*ret_val)); ret_val->elements = talloc_array(ret_val, void*, max_size); if (ret_val->elements == NULL) { talloc_free(ret_val); ret_val = NULL; } else { memset(ret_val->elements, 0, max_size*sizeof(void*)); ret_val->max_size = max_size; ret_val->top = 0; } } return ret_val; } void_stack* void_stack_copy(const void_stack* v) { unsigned int i; void_stack* ret_val; if(v == NULL) return NULL; ret_val = void_stack_new(v->max_size); if(ret_val == NULL) return NULL; for(i = 0; i < v->top; i++) ret_val->elements[i] = v->elements[i]; ret_val->top = v->top; return ret_val; } void_stack* void_stack_copy_reverse(const void_stack* v) { unsigned int i; void_stack* ret_val; if(v == NULL) return NULL; ret_val = void_stack_new(v->max_size); if(ret_val == NULL) return NULL; for(i = 0; i < v->top; i++) ret_val->elements[i] = v->elements[v->top-i-1]; ret_val->top = v->top; return ret_val; } void void_stack_free(void_stack* stack) { talloc_free(stack); } void void_stack_free_deep(void_stack* stack) { unsigned short i; for(i=0; i < stack->top; i++) free(stack->elements[i]); talloc_free(stack); } unsigned short void_stack_size(const void_stack* stack) { return stack->top; } void* void_stack_pop(void_stack* stack) { void* ret_val = NULL; if(stack->top > 0) { ret_val = stack->elements[--(stack->top)]; stack->elements[stack->top] = NULL; } return ret_val; } bool void_stack_push(void_stack* stack, void* e) { if(stack->top < stack->max_size) { stack->elements[stack->top++] = e; return true; } else return false; } const void* void_stack_cur(const void_stack* stack) { void* ret_val = NULL; if(stack->top > 0) ret_val = stack->elements[stack->top-1]; return ret_val; } void_stack_iterator* void_stack_iterator_new(const void_stack* stack) { void_stack_iterator* ret_val = NULL; if(stack != NULL) { ret_val = talloc(stack, void_stack_iterator); if (ret_val != NULL) { ret_val->stack = stack; ret_val->cur = 0; } } return ret_val; } void void_stack_iterator_free(void_stack_iterator* iter) { talloc_free(iter); } const void* void_stack_iterator_next(void_stack_iterator* iter) { if(iter->cur < iter->stack->top) return iter->stack->elements[iter->cur++]; else return NULL; } reglookup+git/python/experimental/utils.py000664 001750 001750 00000043015 12566244653 022236 0ustar00sophiesophie000000 000000 import os, sys, re, pdb import distutils.sysconfig as sysconfig import distutils.util import platform import SCons.SConf as SConf import config # taken from scons wiki def CheckPKGConfig(context, version): context.Message( 'Checking for pkg-config version > %s... ' % version) ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0] context.Result( ret ) return ret def CheckFramework(context, name): ret = 0 if (platform.system().lower() == 'darwin'): context.Message( '\nLooking for framework %s... ' % name ) lastFRAMEWORKS = context.env['FRAMEWORKS'] context.env.Append(FRAMEWORKS = [name]) ret = context.TryLink(""" int main(int argc, char **argv) { return 0; } """, '.c') if not ret: context.env.Replace(FRAMEWORKS = lastFRAMEWORKS ) return ret def CheckFink(context): context.Message( 'Looking for fink... ') prog = context.env.WhereIs('fink') if prog: ret = 1 prefix = prog.rsplit(os.sep, 2)[0] context.env.Append(LIBPATH = [prefix + os.sep +'lib'], CPPPATH = [prefix + os.sep +'include']) context.Message( 'Adding fink lib and include path') else: ret = 0 context.Result(ret) return int(ret) def CheckMacports(context): context.Message( 'Looking for macports... ') prog = context.env.WhereIs('port') if prog: ret = 1 prefix = prog.rsplit(os.sep, 2)[0] context.env.Append(LIBPATH = [prefix + os.sep + 'lib'], CPPPATH = [prefix + os.sep + 'include']) context.Message( 'Adding port lib and include path') else: ret = 0 context.Result(ret) return int(ret) # TODO: We should use the scons one instead def CheckLib(context, name): context.Message( 'Looking for lib %s... ' % name ) lastLIBS = context.env['LIBS'] context.env.Append(LIBS = [name]) ret = context.TryLink(""" int main(int argc, char **argv) { return 0; } """,'.c') if not ret: context.env.Replace(LIBS = lastLIBS) return ret def ConfigPKG(context, name): context.Message( '\nUsing pkg-config for %s... ' % name ) ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] context.Result( ret ) if ret: context.env.ParseConfig('pkg-config --cflags --libs \'%s\'' % name) return int(ret) def CheckPKG(context, name): context.Message( 'Checking for %s... ' % name ) if platform.system().lower() == 'windows': return 0 ret = 1 if not CheckFramework(context, name): if not ConfigPKG(context, name.lower()): ret = CheckLib(context, name) context.Result(ret) return int(ret) ## Configure colors for pretty builds colors = {} colors['cyan'] = '\033[96m' colors['purple'] = '\033[95m' colors['blue'] = '\033[94m' colors['green'] = '\033[92m' colors['yellow'] = '\033[93m' colors['red'] = '\033[91m' colors['end'] = '\033[0m' #If the output is not a terminal, remove the colors if not sys.stdout.isatty(): for key, value in colors.iteritems(): colors[key] = '' def error(msg): print "%s%s%s" % (colors['red'], msg, colors['end']) sys.exit(1) def warn(msg): print "%s%s%s" % (colors['yellow'], msg, colors['end']) compile_source_message = '%sCompiling %s==> %s$SOURCE%s' % \ (colors['blue'], colors['purple'], colors['yellow'], colors['end']) compile_shared_source_message = '%sCompiling shared %s==> %s$SOURCE%s' % \ (colors['blue'], colors['purple'], colors['yellow'], colors['end']) compile_python_source_message = '%sCompiling python module %s==> %s$SOURCE%s' % \ (colors['blue'], colors['purple'], colors['yellow'], colors['end']) link_program_message = '%sLinking Program %s==> %s$TARGET%s' % \ (colors['red'], colors['purple'], colors['yellow'], colors['end']) link_library_message = '%sLinking Static Library %s==> %s$TARGET%s' % \ (colors['red'], colors['purple'], colors['yellow'], colors['end']) ranlib_library_message = '%sRanlib Library %s==> %s$TARGET%s' % \ (colors['red'], colors['purple'], colors['yellow'], colors['end']) link_shared_library_message = '%sLinking Shared Library %s==> %s$TARGET%s' % \ (colors['red'], colors['purple'], colors['yellow'], colors['end']) link_python_module_message = '%sLinking Native Python module %s==> %s${TARGET}%s' % \ (colors['red'], colors['purple'], colors['yellow'], colors['end']) java_library_message = '%sCreating Java Archive %s==> %s$TARGET%s' % \ (colors['red'], colors['purple'], colors['yellow'], colors['end']) def install_colors(args): """ Installs colors into an environment """ args.update(dict( CXXCOMSTR = compile_source_message, CCCOMSTR = compile_source_message, SHCCCOMSTR = compile_shared_source_message, SHCXXCOMSTR = compile_shared_source_message, ARCOMSTR = link_library_message, RANLIBCOMSTR = ranlib_library_message, SHLINKCOMSTR = link_shared_library_message, LINKCOMSTR = link_program_message, JARCOMSTR = java_library_message, JAVACCOMSTR = compile_source_message,)) import optparse ### This workaround is because scons does not provide access to the ### parser, and by setting Help() we are unable to generate the option ### listing from AddOption my_parser = optparse.OptionParser() import SCons.Script.Main as Main import SCons.Script as Script def add_option(arg, option, *args, **kwargs): opt = "--%s" % option Main.AddOption(opt, *args, **kwargs) my_parser.add_option(opt, *args, **kwargs) arg[option] = Main.GetOption(option) def generate_help(vars, env): Script.Help("AFF4 build system configuration.\n\nFollowing are compile time options:\n") Script.Help(my_parser.format_help()) Script.Help("\nThe following variables can be used on the command line:\n") Script.Help(vars.GenerateHelpText(env)) HEADERS = {} def check_size(conf, types): global _DEFAULTS for t in types: name = "SIZEOF_" + t.replace(" ","_").upper() HEADERS[name] = conf.CheckTypeSize( t, size = _DEFAULTS[t][0]) def check_type(conf, types): header = None for t in types: if ':' in t: t, header = t.split(':') define = "HAVE_" + t.upper().replace(".","_") result = 0 if conf.CheckType(t, includes="#include <%s>\n" % header): result = 1 HEADERS[define] = result def check_build(conf, message, define, prog): """ Build and links prog and adds define if that succeeds """ context = SConf.CheckContext(conf) context.Message("Checking for %s ..." % message) if context.TryLink(prog, ".c"): HEADERS[define] = 1 context.Message("yes\n") else: context.Message("no\n") def check(type, conf, headers, extra_include =''): for header in headers: if ":" in header: define, header = header.split(':') else: if "/" in header: tmp = header.split("/")[-1] else: tmp = header define = "HAVE_" + tmp.upper().replace(".","_") global HEADERS result = 0 if type == 'header': #pdb.set_trace() if conf.CheckCHeader(header): result = 1 HEADERS[define] = result elif type == 'func': if conf.CheckFunc(header, header=extra_include): result = 1 HEADERS[define] = result elif type == 'lib': if conf.CheckLib(header): result =1 HEADERS[define] = result ## Build the config.h file def config_h_build(target, source, env): config_h_defines = env.Dictionary() config_h_defines.update(env.config.__dict__) warn("Generating %s" % (target[0].path)) for a_target, a_source in zip(target, source): config_h = file(str(a_target), "w") config_h_in = file(str(a_source), "r") config_h.write(config_h_in.read() % config_h_defines) config_h_in.close() keys = HEADERS.keys() keys.sort() for k in keys: if HEADERS[k]: config_h.write("#define %s %s\n" % (k,HEADERS[k])) else: config_h.write("/** %s unset */\n" % k) config_h.close() import SCons.Environment class ExtendedEnvironment(SCons.Environment.Environment): """ Implementation from Richard Levitte email to org.tigris.scons.dev dated Jan 26, 2006 7:05:10 am.""" python_cppflags = "-I"+sysconfig.get_python_inc() def PythonModule(self, libname, lib_objs=[], **kwargs): """ This builds a python module which is almost a library but is sometimes named differently. We have two modes - a cross compile mode where we do our best to guess the flags. In the native mode we can get the required flags directly from distutils. """ if config.MINGW_XCOMPILE: shlib_suffix = ".pyd" cppflags = "-I%s" % config.XCOMPILE_PYTHON_PATH shlink_flags = [''] else: platform = self.subst('$PLATFORM') shlib_pre_action = None shlib_suffix = distutils.util.split_quoted( sysconfig.get_config_var('SO')) shlib_post_action = None cppflags = distutils.util.split_quoted(self.python_cppflags) shlink_flags = str(self['LINKFLAGS']).split() install_dest = distutils.util.split_quoted( os.path.join( sysconfig.get_config_var('BINLIBDEST'),os.path.dirname(libname))) flags = distutils.util.split_quoted( sysconfig.get_config_var('LDSHARED')) ## For some stupid reason they include the compiler in LDSHARED shlink_flags.extend([x for x in flags if 'gcc' not in x]) shlink_flags.append(sysconfig.get_config_var('LOCALMODLIBS')) ## TODO cross compile mode kwargs['LIBPREFIX'] = '' kwargs['CPPFLAGS'] = cppflags kwargs['SHLIBSUFFIX'] = shlib_suffix kwargs['SHLINKFLAGS'] = shlink_flags if not self.config.V: kwargs['SHCCCOMSTR'] = compile_python_source_message kwargs['SHLINKCOMSTR'] = link_python_module_message lib = self.SharedLibrary(libname,lib_objs, **kwargs) ## Install it to the right spot self.Install(install_dest, lib) self.Alias('install', install_dest) return lib def VersionedSharedLibrary(self, libname, libversion, lib_objs=[]): """ This creates a version library similar to libtool. We name the library with the appropriate soname. """ platform = self.subst('$PLATFORM') shlib_pre_action = None shlib_suffix = self.subst('$SHLIBSUFFIX') shlib_post_action = None shlink_flags = SCons.Util.CLVar(self.subst('$SHLINKFLAGS')) if platform == 'posix': shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ] shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ] shlib_suffix += '.' + libversion shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=${LIBPREFIX}%s%s' % ( libname, shlib_suffix) ] elif platform == 'aix': shlib_pre_action = [ "nm -Pg $SOURCES > ${TARGET}.tmp1", "grep ' [BDT] ' < ${TARGET}.tmp1 > ${TARGET}.tmp2", "cut -f1 -d' ' < ${TARGET}.tmp2 > ${TARGET}", "rm -f ${TARGET}.tmp[12]" ] shlib_pre_action_output_re = [ '$', '.exp' ] shlib_post_action = [ 'rm -f $TARGET', 'ln -s $SOURCE $TARGET' ] shlib_post_action_output_re = [ '%s\\.[0-9\\.]*' % re.escape(shlib_suffix), shlib_suffix ] shlib_suffix += '.' + libversion shlink_flags += ['-G', '-bE:${TARGET}.exp', '-bM:SRE'] elif platform == 'cygwin': shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ] elif platform == 'darwin': shlib_suffix = '.' + libversion + shlib_suffix shlink_flags += [ '-dynamiclib', '-current-version %s' % libversion ] lib = self.SharedLibrary(libname,lib_objs, SHLIBSUFFIX=shlib_suffix, SHLINKFLAGS=shlink_flags) if shlib_pre_action: shlib_pre_action_output = re.sub(shlib_pre_action_output_re[0], shlib_pre_action_output_re[1], str(lib[0])) self.Command(shlib_pre_action_output, [ lib_objs ], shlib_pre_action) self.Depends(lib, shlib_pre_action_output) if shlib_post_action: shlib_post_action_output = re.sub(shlib_post_action_output_re[0], shlib_post_action_output_re[1], str(lib[0])) self.Command(shlib_post_action_output, lib, shlib_post_action) return lib def InstallVersionedSharedLibrary(self, destination, lib): platform = self.subst('$PLATFORM') shlib_suffix = self.subst('$SHLIBSUFFIX') shlib_install_pre_action = None shlib_install_post_action = None if platform == 'posix': shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ] shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ] shlib_install_post_action = shlib_post_action shlib_install_post_action_output_re = shlib_post_action_output_re ilib = self.Install(destination,lib) if shlib_install_pre_action: shlib_install_pre_action_output = re.sub(shlib_install_pre_action_output_re[0], shlib_install_pre_action_output_re[1], str(ilib[0])) self.Command(shlib_install_pre_action_output, ilib, shlib_install_pre_action) self.Depends(shlib_install_pre_action_output, ilib) if shlib_install_post_action: shlib_install_post_action_output = re.sub(shlib_install_post_action_output_re[0], shlib_install_post_action_output_re[1], str(ilib[0])) self.Command(shlib_install_post_action_output, ilib, shlib_install_post_action) import subprocess def pkg_config(pkg, type): try: result = subprocess.Popen(["%s-config" % pkg, "--%s" % type], stdout=subprocess.PIPE).communicate()[0] except: error("Unable to run %s-config - do you have the dev package installed?" % pkg) return result.strip() # Sensible default for common types on common platforms. _DEFAULTS = { 'char': [1,], 'short' : [2,], 'int' : [4, 2], 'long' : [4, 8], 'long long' : [8, 4], # Normally, there is no need to check unsigned types, because they are # guaranteed to be of the same size than their signed counterpart. 'unsigned char': [1,], 'unsigned short' : [2,], 'unsigned int' : [4, 2], 'unsigned long' : [4, 8], 'unsigned long long' : [8, 4], 'float' : [4,], 'double' : [8,], 'long double' : [12,], 'size_t' : [4,], } def CheckTypeSize(context, type, includes = None, language = 'C', size = None): """This check can be used to get the size of a given type, or to check whether the type is of expected size. Arguments: - type : str the type to check - includes : sequence list of headers to include in the test code before testing the type - language : str 'C' or 'C++' - size : int if given, will test wether the type has the given number of bytes. If not given, will test against a list of sizes (all sizes between 0 and 16 bytes are tested). Returns: status : int 0 if the check failed, or the found size of the type if the check succeeded.""" minsz = 0 maxsz = 16 if includes: src = "\n".join([r"#include <%s>\n" % i for i in includes]) else: src = "" if language == 'C': ext = '.c' elif language == 'C++': ext = '.cpp' else: raise NotImplementedError("%s is not a recognized language" % language) # test code taken from autoconf: this is a pretty clever hack to find that # a type is of a given size using only compilation. This speeds things up # quite a bit compared to straightforward code using TryRun src += r""" typedef %s scons_check_type; int main() { static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) <= %d)]; test_array[0] = 0; return 0; } """ if size: # Only check if the given size is the right one context.Message('Checking %s is %d bytes... ' % (type, size)) st = context.TryCompile(src % (type, size), ext) context.Result(st) if st: return size else: return 0 else: # Only check if the given size is the right one context.Message('Checking size of %s ... ' % type) # Try sensible defaults first try: szrange = _DEFAULTS[type] except KeyError: szrange = [] szrange.extend(xrange(minsz, maxsz)) st = 0 # Actual test for sz in szrange: st = context.TryCompile(src % (type, sz), ext) if st: break if st: context.Result('%d' % sz) return sz else: context.Result('Failed !') return 0 #For example, to check wether long is 4 bytes on your platform, you can do: #config.CheckTypeSize('long', size = 4). ## Now check the sizes