#!/usr/local/bin/python ## ## Program to convert RegView diff output to ## a registry file. Hopefully, less buggy than ## the older PERL script. ## ## Author: Jason Trowbridge (ratman@nmt.edu) ## Date: 4/23/99 import sys, string, re REG_OP = 0 REG_KEY = 1 REG_NAME = 2 REG_OLDVAL = 3 REG_VAL = 4 REG_TYPE = 5 VALID_KEYS = ['HKEY_CLASSES_ROOT\\', 'HKEY_LOCAL_MACHINE\\', 'HKEY_USERS\\', 'HKEY_CURRENT_USER\\', 'HKEY_CURRENT_CONFIG\\', 'HKEY_DYN_DATA\\'] warn = sys.stderr.write PrevKey = '' curline = 1 inputname = '' string_typing = 0 def checkkey( key ): 'Returns: List containing matching root keys, or an empty list if no matches.' return filter( lambda x, y=key: string.find(y, x) != -1, VALID_KEYS ) def quote_string( s ): 'Returns: s where backslash (\\), then quote (") is escaped.' return string.replace( string.replace( s, "\\", "\\\\" ), '"', '\\"' ) def getname( reglist ): 'Returns: Name for the keyvalue, or None if no name.' name = None if reglist[REG_NAME] == '' and (reglist[REG_OLDVAL] or reglist[REG_VAL] or reglist[REG_TYPE]): name = '@' elif reglist[REG_NAME]: name = '"' + quote_string(reglist[REG_NAME]) + '"' return name def isdword( s ): """Valid dwords are of the following format: '0xhexval (decval)', where: hexval is an 8 digit hex number decval is the decimal equivalent Windows/RegView use lowercase letters for 0xa through 0xf. """ dwordrex = r'\A0x(?P' + '[a-f0-9]' * 8 + ')\ \((?P-?\d+)\)\Z' m = re.match( dwordrex, s ) if not m: return 0 try: hexval = string.atol( m.group('hexnum'), 16 ) decval = string.atol( m.group('decnum') ) if hexval & 0x80000000: hexval = hexval - 0x100000000L # Take into account *signed longint* except: return 0 return hexval == decval def ishex( s ): """Valid binary datum are of the following format: 'aa aa aa '(ad infinitum), where: aa is an 8 bit hex number Not that we can't tell the difference between an empty binary and an empty string. """ hexrex = r'\A(([a-f0-9][a-f0-9] )*[a-f0-9][a-f0-9])?\Z' return re.match( hexrex, s ) def rv_bugfix( reglist ): """Returns: reglist that does not have the miscellaneous discrepancies from RegView's reg-diff format. Types are resolved as follows: Orig Type: Detected Hex DWord String Empty H H(S) S(H) Hex H H H(S) DWord D(S) D D(S) Unknown S S S """ global string_typing if reglist[REG_OP] == 'Added': ## 'Added' lines don't have value in the right place. ## If the warning pops up, it's probably a sign of a bug ## in either the ishex() or isdword() functions. reglist[REG_VAL] = reglist[REG_OLDVAL] if reglist[REG_TYPE] == 'Binary' and not ishex(reglist[REG_VAL]) or \ reglist[REG_TYPE] == 'DWORD' and not isdword(reglist[REG_VAL]): warn( '%s(%i): Added value is of invalid type! CHECK ME!!!\n' % (inputname, curline) ) elif reglist[REG_TYPE] == 'String' and string_typing: pass elif reglist[REG_OP] == 'Changed': ## Regview gets confuzzled when value types change. ## Note that either String or Binary can have 0 length. if isdword(reglist[REG_VAL]): regtype = 'DWORD' elif len(reglist[REG_VAL]) and ishex(reglist[REG_VAL]): regtype = 'Binary' elif reglist[REG_VAL] == '' and \ reglist[REG_TYPE] in ['Binary', 'DWORD']: regtype = 'Binary' else: regtype = 'String' ## If the detected type don't match the listed type, ## warn & correct the situation if reglist[REG_TYPE] != regtype and reglist[REG_NAME]: if regtype == 'String': temp = 'unknown' else: temp = regtype warn( '%s(%i): Regview used type %s for type %s -- Using type %s\n' % (inputname, curline, reglist[REG_TYPE], temp, regtype) ) reglist[REG_TYPE] = regtype if not reglist[REG_NAME] and regtype != 'String': warn( '%s(%i): Empty name assigned non-String type -- Using type String\n' % (inputname, curline) ) return reglist def gen_regline( reglist ): """Returns: string containing the registry file format generated from reglist. """ ## Check to see if we need to change keys global PrevKey if PrevKey != reglist[REG_KEY]: ret = '\r\n[%s]\r\n' % reglist[REG_KEY] PrevKey = reglist[REG_KEY] if not checkkey( reglist[REG_KEY] ): warn( '%s(%i): Invalid key\n' % (inputname, curline) ) else: ret = '' name = getname( reglist ) if not name: return ret reglist = rv_bugfix( reglist ) ## Format according to type. if reglist[REG_TYPE] == 'Binary': value = 'hex:' + string.join( string.split(reglist[REG_VAL]), ',' ) elif reglist[REG_TYPE] == 'DWORD': value = 'dword:' + string.split(reglist[REG_VAL])[0][2:] else: value = '"' + quote_string( reglist[REG_VAL] ) + '"' ret = ret + '%s=%s\r\n' % (name, value) return ret def parse_diffline( input ): "Returns: regedit version of Regview's reg-diff string input." regparam = string.split( string.strip(input), '", "' ) ret = '' if len(regparam) >= 6: ## Strip quotes off first & last params. regparam[0] = regparam[0][1:] regparam[-1] = regparam[-1][:-1] if len(regparam) > 6: # If there's more than 6 splits, we have a weird string value warn( '%s(%i): Strange registry line -- Please check\n' % (inputname, curline) ) ## Depending on the registry operation, do different things. if regparam[REG_OP] in ['Changed', 'Added']: ret = gen_regline( regparam ) elif regparam[REG_OP] == 'Deleted': ## Can't do anything about deleted keys but warn warn( '%s(%i): Deleted value %s (%s)\n' % (inputname, curline, getname( regparam ), regparam[REG_KEY]) ) else: warn( '%s(%i): Unknown registry operation: %s\n' % (inputname, curline, regparam[REG_OP]) ) else: warn( '%s(%i): Incomplete registry line\n' % (inputname, curline) ) return ret def parsediff( inf, outf ): """Parse file object Regview reg-diff file inf into a Windows '95 registry file outf.""" global curline input = inf.readline() # Throw out RegView diff header DIFF_HEADER = ['Status, Key, Value Name, Ori. Value Data, New Value Data, Type', '--------------------------------------------------------------'] while DIFF_HEADER and string.strip(input) == DIFF_HEADER[0]: del DIFF_HEADER[0] curline = curline + 1 input = inf.readline() if DIFF_HEADER: warn( '%s: Missing or incomplete RegView header\n' % inputname ) # Parse the input file while input != '': if string.strip(input) != '': output = parse_diffline( input ) if output: outf.write( output ) # Next line of input input = inf.readline() curline = curline + 1 return def difftoreg( inf=sys.stdin, outf=sys.stdout ): global inputname if type(inf) not in (type(sys.stdin), type('')): raise TypeError, 'inf should be a file or filename' if type(outf) not in (type(sys.stdin), type('')): raise TypeError, 'outf should be a file or filename' if type(inf) == type(''): inf = open( inf, 'r' ) if type(outf) == type(''): outf = open( outf, 'wb' ) # Dos needs \r\n's inputname = inf.name outf.write( 'REGEDIT4\r\n' ) # Registry Header parsediff( inf, outf ) return def showhelp(): print """Usage: %s [-s] [(input file) [(outputfile)] -s -- Enable string retyping. If not specified strings will not be retyped, even if the data matches the format of another data type. (input file) -- A RegView registry diff file. Defaults to stdin if not specified. (output file) -- File to put Windows registry in. Defaults to stdout if not specified.""" % sys.argv[0] return if __name__ == '__main__': if '-h' in sys.argv or '--help' in sys.argv: showhelp() sys.exit(0) if '-s' in sys.argv: sys.argv = filter( lambda x : x != '-s', sys.argv ) string_typing = 1 if len(sys.argv) == 1: difftoreg() elif len(sys.argv) == 2: difftoreg(sys.argv[1]) else: difftoreg(sys.argv[1], sys.argv[2])