5030c249d63b3e65e2130dbb128fed82e5de0c65
[hivex.git] / xml / hivexml.c
1 /* hivexml - Convert Windows Registry "hive" to XML file.
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdint.h>
25 #include <inttypes.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <locale.h>
30
31 #ifdef HAVE_LIBINTL_H
32 #include <libintl.h>
33 #endif
34
35 #include <libxml/xmlwriter.h>
36
37 #include "hivex.h"
38
39 #ifdef HAVE_GETTEXT
40 #include "gettext.h"
41 #define _(str) dgettext(PACKAGE, (str))
42 //#define N_(str) dgettext(PACKAGE, (str))
43 #else
44 #define _(str) str
45 //#define N_(str) str
46 #endif
47
48 static char *filetime_to_8601 (int64_t windows_ticks);
49
50 /* Callback functions. */
51 static int node_start (hive_h *, void *, hive_node_h, const char *name);
52 static int node_end (hive_h *, void *, hive_node_h, const char *name);
53 static int value_string (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
54 static int value_multiple_strings (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv);
55 static int value_string_invalid_utf16 (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
56 static int value_dword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int32_t);
57 static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int64_t);
58 static int value_binary (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
59 static int value_none (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
60 static int value_other (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
61
62 static struct hivex_visitor visitor = {
63   .node_start = node_start,
64   .node_end = node_end,
65   .value_string = value_string,
66   .value_multiple_strings = value_multiple_strings,
67   .value_string_invalid_utf16 = value_string_invalid_utf16,
68   .value_dword = value_dword,
69   .value_qword = value_qword,
70   .value_binary = value_binary,
71   .value_none = value_none,
72   .value_other = value_other
73 };
74
75 #define XML_CHECK(proc, args)                                           \
76   do {                                                                  \
77     if ((proc args) == -1) {                                            \
78       fprintf (stderr, _("%s: failed to write XML document\n"), #proc); \
79       exit (EXIT_FAILURE);                                              \
80     }                                                                   \
81   } while (0)
82
83 int
84 main (int argc, char *argv[])
85 {
86   setlocale (LC_ALL, "");
87 #ifdef HAVE_BINDTEXTDOMAIN
88   bindtextdomain (PACKAGE, LOCALEBASEDIR);
89   textdomain (PACKAGE);
90 #endif
91
92   int c;
93   int open_flags = 0;
94   int visit_flags = 0;
95
96   while ((c = getopt (argc, argv, "dk")) != EOF) {
97     switch (c) {
98     case 'd':
99       open_flags |= HIVEX_OPEN_DEBUG;
100       break;
101     case 'k':
102       visit_flags |= HIVEX_VISIT_SKIP_BAD;
103       break;
104     default:
105       fprintf (stderr, "hivexml [-dk] regfile > output.xml\n");
106       exit (EXIT_FAILURE);
107     }
108   }
109
110   if (optind + 1 != argc) {
111     fprintf (stderr, _("hivexml: missing name of input file\n"));
112     exit (EXIT_FAILURE);
113   }
114
115   hive_h *h = hivex_open (argv[optind], open_flags);
116   if (h == NULL) {
117     perror (argv[optind]);
118     exit (EXIT_FAILURE);
119   }
120
121   /* Note both this macro, and xmlTextWriterStartDocument leak memory.  There
122    * doesn't seem to be any way to recover that memory, but it's not a
123    * large amount.
124    */
125   LIBXML_TEST_VERSION;
126
127   xmlTextWriterPtr writer;
128   writer = xmlNewTextWriterFilename ("/dev/stdout", 0);
129   if (writer == NULL) {
130     fprintf (stderr, _("xmlNewTextWriterFilename: failed to create XML writer\n"));
131     exit (EXIT_FAILURE);
132   }
133
134   XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL));
135   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive"));
136
137   int64_t hive_mtime = hivex_last_modified (h);
138   if (hive_mtime >= 0) {
139     char *timebuf = filetime_to_8601 (hive_mtime);
140     if (timebuf) {
141       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
142       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
143       XML_CHECK (xmlTextWriterEndElement, (writer));
144       free (timebuf);
145     }
146   }
147
148   if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) {
149     perror (argv[optind]);
150     exit (EXIT_FAILURE);
151   }
152
153   if (hivex_close (h) == -1) {
154     perror (argv[optind]);
155     exit (EXIT_FAILURE);
156   }
157
158   XML_CHECK (xmlTextWriterEndElement, (writer));
159   XML_CHECK (xmlTextWriterEndDocument, (writer));
160   xmlFreeTextWriter (writer);
161
162   exit (EXIT_SUCCESS);
163 }
164
165 /* Convert Windows filetime to ISO 8601 format.
166  * http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux/6161842#6161842
167  *
168  * Source for time_t->char* conversion: Fiwalk version 0.6.14's
169  * fiwalk.cpp.
170  *
171  * The caller should free the returned buffer.
172  *
173  * This function returns NULL on a 0 input.  In the context of
174  * hives, which only have mtimes, 0 will always be a complete
175  * absence of data.
176  */
177
178 #define WINDOWS_TICK 10000000LL
179 #define SEC_TO_UNIX_EPOCH 11644473600LL
180 #define TIMESTAMP_BUF_LEN 32
181
182 static char *
183 filetime_to_8601 (int64_t windows_ticks)
184 {
185   char *ret;
186   time_t t;
187   struct tm *tm;
188
189   if (windows_ticks == 0LL)
190     return NULL;
191
192   t = windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH;
193   tm = gmtime (&t);
194   if (tm == NULL)
195     return NULL;
196
197   ret = malloc (TIMESTAMP_BUF_LEN);
198   if (ret == NULL) {
199     perror ("malloc");
200     exit (EXIT_FAILURE);
201   }
202
203   if (strftime (ret, TIMESTAMP_BUF_LEN, "%FT%TZ", tm) == 0) {
204     perror ("strftime");
205     exit (EXIT_FAILURE);
206   }
207
208   return ret;
209 }
210
211 static int
212 node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
213 {
214   int64_t last_modified;
215   char *timebuf;
216   int ret = 0;
217
218   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
219   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
220   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
221
222   if (node == hivex_root (h)) {
223     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "root", BAD_CAST "1"));
224   }
225
226   last_modified = hivex_node_timestamp (h, node);
227   if (last_modified >= 0) {
228     timebuf = filetime_to_8601 (last_modified);
229     if (timebuf) {
230       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
231       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
232       XML_CHECK (xmlTextWriterEndElement, (writer));
233       free (timebuf);
234     }
235   }
236
237   return 0;
238 }
239
240 static int
241 node_end (hive_h *h, void *writer_v, hive_node_h node, const char *name)
242 {
243   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
244   XML_CHECK (xmlTextWriterEndElement, (writer));
245   return 0;
246 }
247
248 static void
249 start_value (xmlTextWriterPtr writer,
250              const char *key, const char *type, const char *encoding)
251 {
252   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "value"));
253   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "type", BAD_CAST type));
254   if (encoding)
255     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "encoding", BAD_CAST encoding));
256   if (*key)
257     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "key", BAD_CAST key));
258   else                          /* default key */
259     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "default", BAD_CAST "1"));
260 }
261
262 static void
263 end_value (xmlTextWriterPtr writer)
264 {
265   XML_CHECK (xmlTextWriterEndElement, (writer));
266 }
267
268 static int
269 value_string (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
270               hive_type t, size_t len, const char *key, const char *str)
271 {
272   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
273   const char *type;
274
275   switch (t) {
276   case hive_t_string: type = "string"; break;
277   case hive_t_expand_string: type = "expand"; break;
278   case hive_t_link: type = "link"; break;
279
280   case hive_t_none:
281   case hive_t_binary:
282   case hive_t_dword:
283   case hive_t_dword_be:
284   case hive_t_multiple_strings:
285   case hive_t_resource_list:
286   case hive_t_full_resource_description:
287   case hive_t_resource_requirements_list:
288   case hive_t_qword:
289     abort ();                   /* internal error - should not happen */
290
291   default:
292     type = "unknown";
293   }
294
295   start_value (writer, key, type, NULL);
296   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
297   XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST str));
298   XML_CHECK (xmlTextWriterEndAttribute, (writer));
299   end_value (writer);
300   return 0;
301 }
302
303 static int
304 value_multiple_strings (hive_h *h, void *writer_v, hive_node_h node,
305                         hive_value_h value, hive_type t, size_t len,
306                         const char *key, char **argv)
307 {
308   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
309   start_value (writer, key, "string-list", NULL);
310
311   size_t i;
312   for (i = 0; argv[i] != NULL; ++i) {
313     XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "string"));
314     XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST argv[i]));
315     XML_CHECK (xmlTextWriterEndElement, (writer));
316   }
317
318   end_value (writer);
319   return 0;
320 }
321
322 static int
323 value_string_invalid_utf16 (hive_h *h, void *writer_v, hive_node_h node,
324                             hive_value_h value, hive_type t, size_t len,
325                             const char *key,
326                             const char *str /* original data */)
327 {
328   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
329   const char *type;
330
331   switch (t) {
332   case hive_t_string: type = "bad-string"; break;
333   case hive_t_expand_string: type = "bad-expand"; break;
334   case hive_t_link: type = "bad-link"; break;
335   case hive_t_multiple_strings: type = "bad-string-list"; break;
336
337   case hive_t_none:
338   case hive_t_binary:
339   case hive_t_dword:
340   case hive_t_dword_be:
341   case hive_t_resource_list:
342   case hive_t_full_resource_description:
343   case hive_t_resource_requirements_list:
344   case hive_t_qword:
345     abort ();                   /* internal error - should not happen */
346
347   default:
348     type = "unknown";
349   }
350
351   start_value (writer, key, type, "base64");
352   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
353   XML_CHECK (xmlTextWriterWriteBase64, (writer, str, 0, len));
354   XML_CHECK (xmlTextWriterEndAttribute, (writer));
355   end_value (writer);
356
357   return 0;
358 }
359
360 static int
361 value_dword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
362              hive_type t, size_t len, const char *key, int32_t v)
363 {
364   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
365   start_value (writer, key, "int32", NULL);
366   XML_CHECK (xmlTextWriterWriteFormatAttribute, (writer, BAD_CAST "value", "%" PRIi32, v));
367   end_value (writer);
368   return 0;
369 }
370
371 static int
372 value_qword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
373              hive_type t, size_t len, const char *key, int64_t v)
374 {
375   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
376   start_value (writer, key, "int64", NULL);
377   XML_CHECK (xmlTextWriterWriteFormatAttribute, (writer, BAD_CAST "value", "%" PRIi64, v));
378   end_value (writer);
379   return 0;
380 }
381
382 static int
383 value_binary (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
384               hive_type t, size_t len, const char *key, const char *v)
385 {
386   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
387   start_value (writer, key, "binary", "base64");
388   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
389   XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
390   XML_CHECK (xmlTextWriterEndAttribute, (writer));
391   end_value (writer);
392   return 0;
393 }
394
395 static int
396 value_none (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
397             hive_type t, size_t len, const char *key, const char *v)
398 {
399   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
400   start_value (writer, key, "none", "base64");
401   if (len > 0) {
402     XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
403     XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
404     XML_CHECK (xmlTextWriterEndAttribute, (writer));
405   }
406   end_value (writer);
407   return 0;
408 }
409
410 static int
411 value_other (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
412              hive_type t, size_t len, const char *key, const char *v)
413 {
414   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
415   const char *type;
416
417   switch (t) {
418   case hive_t_none:
419   case hive_t_binary:
420   case hive_t_dword:
421   case hive_t_dword_be:
422   case hive_t_qword:
423   case hive_t_string:
424   case hive_t_expand_string:
425   case hive_t_link:
426   case hive_t_multiple_strings:
427     abort ();                   /* internal error - should not happen */
428
429   case hive_t_resource_list: type = "resource-list"; break;
430   case hive_t_full_resource_description: type = "resource-description"; break;
431   case hive_t_resource_requirements_list: type = "resource-requirements"; break;
432
433   default:
434     type = "unknown";
435   }
436
437   start_value (writer, key, type, "base64");
438   if (len > 0) {
439     XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
440     XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
441     XML_CHECK (xmlTextWriterEndAttribute, (writer));
442   }
443   end_value (writer);
444
445   return 0;
446 }