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