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