66require 'hivex'
77require 'paint'
88
9+ # Extracting the contents of Microsoft Windows Registry (hive) and display it as a colorful tree but mainly focused on
10+ # parsing BCD files to extract WIM files path for PXE attacks.
911class HivExcavator
12+ # Windows Registry Value Type
13+ # @see https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.registryvaluekind?view=net-7.0
1014 TYPE = {
11- 0 => 'none' ,
15+ -1 => 'none' ,
16+ 0 => 'unknown' ,
1217 1 => 'string' ,
1318 2 => 'expandstring' ,
19+ 3 => 'binary' ,
1420 4 => 'dword' , # hex
21+ 7 => 'multistring' ,
1522 11 => 'qword' # hex
1623 } . freeze
1724
25+ # Color palette of HivExcavator for display
1826 PALETTE = {
1927 MAIN : '#fe218b' , # 70d6ff
2028 SECOND : '#fed700' , # ff70a6
2129 THIRD : '#21b0fe' , # ff9770
2230 FOURTH : '#06d6a0' # ffd670
2331 } . freeze
2432
33+ # Instantiate HivExcavator
34+ # @param hive [String|Hivex::Hivex] Can be either a file path to a BCD file +String+ or a Hivex (hive)
35+ # instance +Hivex::Hivex+.
2536 def initialize ( hive )
2637 case hive
2738 when Hivex ::Hivex
@@ -32,35 +43,56 @@ def initialize(hive)
3243 end
3344
3445 # Does a node has children?
46+ # @param hive [Hivex::Hivex] hive instance
47+ # @param node [Integer] node index
48+ # @return [Boolean]
3549 def self . node_children? ( hive , node )
3650 !hive . node_children ( node ) . empty?
3751 end
3852
53+ # Does a node has children?
54+ # @param node [Integer] node index
55+ # @return [Boolean]
3956 def node_children? ( node )
4057 HivExcavator . node_children? ( @hive , node )
4158 end
4259
4360 # Does a node has a parent?
61+ # @param hive [Hivex::Hivex] hive instance
62+ # @param node [Integer] node index
63+ # @return [Boolean]
4464 def self . node_parent? ( hive , node )
4565 hive . node_parent ( node ) . integer?
4666 rescue Hivex ::Error # Bad address
4767 false
4868 end
4969
70+ # Does a node has a parent?
71+ # @param node [Integer] node index
72+ # @return [Boolean]
5073 def node_parent? ( node )
5174 HivExcavator . node_parent? ( @hive , node )
5275 end
5376
5477 # Does a node has values?
78+ # @param hive [Hivex::Hivex] hive instance
79+ # @param node [Integer] node index
80+ # @return [Boolean]
5581 def self . node_values? ( hive , node )
5682 !hive . node_values ( node ) . empty?
5783 end
5884
85+ # Does a node has values?
86+ # @param node [Integer] node index
87+ # @return [Boolean]
5988 def node_values? ( node )
6089 HivExcavator . node_values? ( @hive , node )
6190 end
6291
6392 # Calculate the depth (nesting level) of a node (from the root node)
93+ # @param hive [Hivex::Hivex] hive instance
94+ # @param node [Integer] node index
95+ # @return [Integer] depth level
6496 def self . node_depth ( hive , node )
6597 count = 0
6698 parent = node
@@ -71,20 +103,34 @@ def self.node_depth(hive, node)
71103 count
72104 end
73105
106+ # Calculate the depth (nesting level) of a node (from the root node)
107+ # @param node [Integer] node index
108+ # @return [Integer] depth level
74109 def node_depth ( node )
75110 HivExcavator . node_depth ( @hive , node )
76111 end
77112
78113 # Output a number of whitespace depending on the depth
114+ # @param hive [Hivex::Hivex] hive instance
115+ # @param node [Integer] node index
116+ # @param spaces [Integer] number of whitespaces per level
117+ # @return [String] whitespaces
79118 def self . space_depth ( hive , node , spaces = 2 )
80119 ' ' * spaces * node_depth ( hive , node )
81120 end
82121
122+ # Output a number of whitespace depending on the depth
123+ # @param node [Integer] node index
124+ # @param spaces [Integer] number of whitespaces per level
125+ # @return [String] whitespaces
83126 def space_depth ( node , spaces = 2 )
84127 HivExcavator . space_depth ( @hive , node , spaces )
85128 end
86129
87130 # Try to resolve known types to extract the value else just fix encoding of the provided value
131+ # @param hive [Hivex::Hivex] hive instance
132+ # @param value [Integer] value index
133+ # @return [String] The decoded value
88134 def self . extract_value ( hive , value )
89135 value_type = TYPE [ hive . value_type ( value ) [ :type ] ]
90136 case value_type
@@ -99,19 +145,17 @@ def self.extract_value(hive, value)
99145 end
100146 end
101147
148+ # Try to resolve known types to extract the value else just fix encoding of the provided value
149+ # @param value [Integer] value index
150+ # @return [String] The decoded value
102151 def extract_value ( value )
103152 HivExcavator . extract_value ( @hive , value )
104153 end
105154
106155 # Display the BCD file as a tree
107- # @example
108- # require 'hivexcavator'
109- # require 'hivex'
110- #
111- # h = Hivex.open('/home/noraj/test/pxe/conf.bcd', {})
112- # root = h.root()
113- # puts "#{h.node_name(root)} (#{root})"
114- # HivExcavator.new(h).node_list(root)
156+ # @param hive [Hivex::Hivex] hive instance
157+ # @param current_node [Integer] node index
158+ # @return [nil]
115159 def self . node_list ( hive , current_node )
116160 nodes = hive . node_children ( current_node )
117161 nodes . each do |node |
@@ -127,30 +171,50 @@ def self.node_list(hive, current_node)
127171 end
128172 node_list ( hive , node ) if node_children? ( hive , node )
129173 end
174+ nil
130175 end
131176
177+ # Display the BCD file as a tree
178+ # @param current_node [Integer] node index
179+ # @return [nil]
132180 def node_list ( current_node )
133181 HivExcavator . node_list ( @hive , current_node )
134182 end
135183
184+ # Display a line with the name of the store / hive
185+ # @param hive [Hivex::Hivex] hive instance
186+ # @return [nil]
136187 def self . diplay_store ( hive )
137188 root = hive . root
138189 puts "#{ Paint [ hive . node_name ( root ) , PALETTE [ :MAIN ] ] } (#{ Paint [ root , PALETTE [ :SECOND ] ] } )"
139190 end
140191
192+ # Display a line with the name of the store / hive
193+ # @return [nil]
141194 def diplay_store
142195 HivExcavator . diplay_store ( @hive )
143196 end
144197
198+ # Display the tree of all nodes, key and values
199+ # @param hive [Hivex::Hivex] hive instance
200+ # @return [nil]
145201 def self . display_tree ( hive )
146202 node_list ( hive , hive . root )
147203 end
148204
205+ # Display the tree of all nodes, key and values
206+ # @return [nil]
149207 def display_tree
150208 HivExcavator . display_tree ( @hive )
151209 end
152210
211+ # Display the store name ({diplay_store}) and the tree ({display_tree})
212+ # @param hive [Hivex::Hivex] hive instance
213+ # @return [nil]
153214 # @example
215+ # require 'hivexcavator'
216+ # require 'hivex'
217+ #
154218 # store = Hivex.open('/home/noraj/test/pxe/conf.bcd', {})
155219 # HivExcavator.display(store)
156220 # store.close
@@ -159,7 +223,12 @@ def self.display(hive)
159223 display_tree ( hive )
160224 end
161225
226+ # Display the store name ({diplay_store}) and the tree ({display_tree})
227+ # @return [nil]
162228 # @example
229+ # require 'hivexcavator'
230+ # require 'hivex'
231+ #
163232 # store = '/home/noraj/test/pxe/conf.bcd'
164233 # hiex = HivExcavator.new(store)
165234 # hiex.display
0 commit comments