1 /*
2  * crashlog.c
3  *
4  * Copyright (C) 2003, 2004, 2005, 2007, 2010, 2011 Rob Caelers <robc@krandor.nl>
5  * Copyright (C) 2007 Ray Satiro <raysatiro@yahoo.com>
6  * All rights reserved.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Based on Dr. Mingw. and OpenTTD
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 
32 #include "crashlog.h"
33 #include "harpoon.h"
34 
35 #include <fcntl.h>
36 
37 #ifndef _O_APPEND
38 #define _O_APPEND       0x0008
39 #endif
40 #ifndef _O_TEXT
41 #define _O_TEXT         0x4000
42 #endif
43 
44 
45 static void unwind_stack(FILE *log, HANDLE process, PCONTEXT context);
46 static void dump_registers(FILE *log, PCONTEXT context);
47 static void dump_registry(FILE *log, HKEY key, char *name);
48 /* static void print_module_list(FILE *log); */
49 
50 static
GetModuleBase(DWORD dwAddress)51 DWORD GetModuleBase(DWORD dwAddress)
52 {
53   MEMORY_BASIC_INFORMATION Buffer;
54 
55   return VirtualQuery((LPCVOID) dwAddress, &Buffer, sizeof(Buffer)) ? (DWORD) Buffer.AllocationBase : 0;
56 }
57 
58 static EXCEPTION_DISPOSITION __cdecl
double_exception_handler(struct _EXCEPTION_RECORD * exception_record,void * establisher_frame,struct _CONTEXT * context_record,void * dispatcher_context)59 double_exception_handler(struct _EXCEPTION_RECORD *exception_record,
60                          void *establisher_frame,
61                          struct _CONTEXT *context_record,
62                          void *dispatcher_context)
63 {
64   (void) exception_record;
65   (void) establisher_frame;
66   (void) context_record;
67   (void) dispatcher_context;
68 
69   MessageBox(NULL,
70              "Workrave has unexpectedly crashed and failed to create a crash "
71              "log. This is serious. Please report this to crashes@workrave.org or "
72              "file a bugreport at: http://issues.workrave.org/. " , "Double exception", MB_OK);
73 
74   exit(1);
75 }
76 
exception_filter(EXCEPTION_POINTERS * ep)77 LONG WINAPI exception_filter(EXCEPTION_POINTERS *ep)
78 {
79   return exception_handler(ep->ExceptionRecord, NULL, ep->ContextRecord, NULL);
80 }
81 
82 EXCEPTION_DISPOSITION __cdecl
exception_handler(struct _EXCEPTION_RECORD * exception_record,void * establisher_frame,struct _CONTEXT * context_record,void * dispatcher_context)83 exception_handler(struct _EXCEPTION_RECORD *exception_record,
84                   void *establisher_frame,
85                   struct _CONTEXT *context_record,
86                   void *dispatcher_context)
87 {
88   char crash_log_name[MAX_PATH];
89   char crash_text[1024];
90   TCHAR szModule[MAX_PATH];
91   HMODULE hModule;
92 
93   FILE *log;
94 
95   (void) establisher_frame;
96   (void) dispatcher_context;
97 
98   __try1(double_exception_handler);
99 
100   harpoon_unblock_input();
101 
102 /*
103  Modified for Unicode >= WinNT. No UnicoWS check for Me/98/95.
104  jay satiro, workrave project, july 2007
105 */
106   WCHAR env_var[ 20 ] = { 0, };
107   WCHAR crashlog[] = L"\\workrave-crashlog.txt";
108   WCHAR *wbuffer = NULL;
109   WCHAR *p_wbuffer = NULL;
110 
111   DWORD ( WINAPI *GetEnvironmentVariableW ) ( LPCWSTR, LPWSTR, DWORD );
112   HANDLE ( WINAPI *CreateFileW ) ( LPCWSTR, DWORD, DWORD,
113     LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE );
114 
115   GetEnvironmentVariableW = ( DWORD ( WINAPI * ) ( LPCWSTR, LPWSTR, DWORD ) )
116     GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetEnvironmentVariableW" );
117   CreateFileW = ( HANDLE ( WINAPI * ) ( LPCWSTR, DWORD, DWORD,
118     LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE ) )
119       GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "CreateFileW" );
120 
121   if( GetEnvironmentVariableW && CreateFileW )
122   // >= WinNT
123     {
124       HANDLE handle;
125       DWORD bufsize, ret;
126 
127       wcsncpy( env_var, L"APPDATA", 19 );
128       env_var[ 19 ] = '\0';
129       bufsize = ( *GetEnvironmentVariableW ) ( env_var, NULL, 0 );
130       // bufsize is size in wide chars, including null
131 
132       if( !bufsize )
133       // If %appdata% is unsuitable, try temp:
134       {
135           wcsncpy( env_var, L"TEMP", 19 );
136           env_var[ 19 ] = '\0';
137           bufsize = ( *GetEnvironmentVariableW ) ( env_var, NULL, 0 );
138       }
139 
140       ret = 0;
141       wbuffer = NULL;
142 
143       if( bufsize )
144         {
145           // We will need room for \\?\ so add 4
146           if( (wbuffer = (WCHAR *)calloc( 4 + bufsize + wcslen( crashlog ), sizeof( WCHAR ) ) ) != NULL)
147             {
148               wcscpy( wbuffer, L"\\\\?\\" );
149               p_wbuffer = wbuffer + 4;
150               ret = ( *GetEnvironmentVariableW ) ( env_var, p_wbuffer, bufsize );
151             }
152         }
153 
154       if( !ret )
155       // Environment unsuitable, notify & terminate.
156         {
157           free( wbuffer );
158           snprintf(crash_text, 1023,
159             "Workrave has unexpectedly crashed. The environment is "
160             "unsuitable to create a crash log. Please file a bug report:\n"
161             "http://issues.workrave.org/\n"
162             "Thanks.");
163           MessageBoxA( NULL, crash_text, "Exception", MB_OK );
164           __except1;
165           exit( 1 );
166         }
167 
168       //last wchar
169       p_wbuffer = wbuffer + wcslen(wbuffer) - 1;
170 
171       while( *p_wbuffer == L'\\' )
172       // remove trailing slashes
173         {
174           *p_wbuffer-- = L'\0';
175         }
176 
177       // append filename to end of string
178       wcscpy( ++p_wbuffer, crashlog );
179 
180 
181       // compare first wchar of returned environment string
182       if( wbuffer[ 4 ] == L'\\' )
183       /*
184       If possible network path, don't include literal \\?\
185       \\?\\\1.2.3.4\workrave-crashlog.txt should be
186       \\1.2.3.4\workrave-crashlog.txt
187       */
188           p_wbuffer = wbuffer + 4;
189       else
190       // Point to start of wbuffer:
191           p_wbuffer = wbuffer;
192 
193 
194       handle = ( *CreateFileW ) (
195           p_wbuffer,
196           GENERIC_READ | GENERIC_WRITE,
197           FILE_SHARE_READ,
198           NULL,
199           CREATE_ALWAYS,
200           FILE_ATTRIBUTE_NORMAL,
201           NULL
202         );
203 
204       int fd = _open_osfhandle( (intptr_t) handle, _O_APPEND | _O_TEXT );
205       log = _fdopen( fd, "w" );
206     }
207   else  // if( GetVersion() & (DWORD) 0x80000000 )
208   // Windows Me/98/95
209     {
210       GetModuleFileName(GetModuleHandle(NULL), crash_log_name, sizeof(crash_log_name));
211       // crash_log_name == c:\program files\workrave\lib\workrave.exe
212       char *s = strrchr(crash_log_name, '\\');
213       assert (s);
214       *s = '\0';
215       // crash_log_name == c:\program files\workrave\lib
216       s = strrchr(crash_log_name, '\\');
217       assert (s);
218       *s = '\0';
219       // crash_log_name == c:\program files\workrave
220       strcat(crash_log_name, "\\workrave-crashlog.txt");
221 
222       log = fopen(crash_log_name, "w");
223     }
224 
225     if( log == NULL )
226       // workrave-crashlog.txt wasn't created.
227       {
228         snprintf(crash_text, 1023,
229           "Workrave has unexpectedly crashed. An attempt to create "
230           "a crashlog has failed. Please file a bug report:\n"
231           "http://issues.workrave.org/\n"
232           "Thanks.");
233         MessageBoxA( NULL, crash_text, "Exception", MB_OK );
234         __except1;
235         exit( 1 );
236       }
237 
238   SYSTEMTIME SystemTime;
239 
240   GetLocalTime(&SystemTime);
241   fprintf(log, "Crash log created on %02d/%02d/%04d at %02d:%02d:%02d.\n\n",
242           SystemTime.wDay,
243           SystemTime.wMonth,
244           SystemTime.wYear,
245           SystemTime.wHour,
246           SystemTime.wMinute,
247           SystemTime.wSecond);
248 
249   fprintf(log, "version = %s\n", VERSION);
250   fprintf(log, "compile date = %s\n", __DATE__);
251   fprintf(log, "compile time = %s\n", __TIME__);
252   fprintf(log, "features = "
253 #ifdef HAVE_DISTRIBUTION
254           "DISTRIBUTION "
255 #endif
256 #ifdef HAVE_EXERCISES
257           "EXERCISES "
258 #endif
259 #ifdef HAVE_GCONF
260           "GCONF?? "
261 #endif
262 #ifdef HAVE_GDOME
263           "GDOME "
264 #endif
265 #ifdef HAVE_GNET
266           "GNET "
267 #endif
268 #ifdef HAVE_GNET2
269           "GNET2 "
270 #endif
271 #ifdef HAVE_XRECORD
272           "XRECORD?? "
273 #endif
274 #ifndef NDEBUG
275           "DEBUG "
276 #endif
277           "\n"
278           );
279 
280 // write locale info:
281   char *buffer = NULL;
282   int bufsize =
283       GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SENGLANGUAGE, buffer, 0);
284 
285   if( bufsize )
286       buffer = (char *)calloc( bufsize + 1, 1 );
287 
288   if( buffer )
289     {
290       GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SENGLANGUAGE, buffer, bufsize);
291       buffer[ bufsize ] = '\0';
292       fprintf( log, "locale = %s\n", buffer );
293       free( buffer );
294     }
295 
296   fprintf(log, "\n\n");
297   fprintf(log, "code = %x\n", (int) exception_record->ExceptionCode);
298   fprintf(log, "flags = %x\n", (int) exception_record->ExceptionFlags);
299   fprintf(log, "address = %x\n", (int) exception_record->ExceptionAddress);
300   fprintf(log, "params = %d\n", (int) exception_record->NumberParameters);
301 
302   fprintf(log, "%s caused ",  GetModuleFileName(NULL, szModule, MAX_PATH) ? szModule : "Application");
303   switch (exception_record->ExceptionCode)
304     {
305     case EXCEPTION_ACCESS_VIOLATION:
306       fprintf(log, "an Access Violation");
307       break;
308 
309     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
310       fprintf(log, "an Array Bound Exceeded");
311       break;
312 
313     case EXCEPTION_BREAKPOINT:
314       fprintf(log, "a Breakpoint");
315       break;
316 
317     case EXCEPTION_DATATYPE_MISALIGNMENT:
318       fprintf(log, "a Datatype Misalignment");
319       break;
320 
321     case EXCEPTION_FLT_DENORMAL_OPERAND:
322       fprintf(log, "a Float Denormal Operand");
323       break;
324 
325     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
326       fprintf(log, "a Float Divide By Zero");
327       break;
328 
329     case EXCEPTION_FLT_INEXACT_RESULT:
330       fprintf(log, "a Float Inexact Result");
331       break;
332 
333     case EXCEPTION_FLT_INVALID_OPERATION:
334       fprintf(log, "a Float Invalid Operation");
335       break;
336 
337     case EXCEPTION_FLT_OVERFLOW:
338       fprintf(log, "a Float Overflow");
339       break;
340 
341     case EXCEPTION_FLT_STACK_CHECK:
342       fprintf(log, "a Float Stack Check");
343       break;
344 
345     case EXCEPTION_FLT_UNDERFLOW:
346       fprintf(log, "a Float Underflow");
347       break;
348 
349     case EXCEPTION_GUARD_PAGE:
350       fprintf(log, "a Guard Page");
351       break;
352 
353     case EXCEPTION_ILLEGAL_INSTRUCTION:
354       fprintf(log, "an Illegal Instruction");
355       break;
356 
357     case EXCEPTION_IN_PAGE_ERROR:
358       fprintf(log, "an In Page Error");
359       break;
360 
361     case EXCEPTION_INT_DIVIDE_BY_ZERO:
362       fprintf(log, "an Integer Divide By Zero");
363       break;
364 
365     case EXCEPTION_INT_OVERFLOW:
366       fprintf(log, "an Integer Overflow");
367       break;
368 
369     case EXCEPTION_INVALID_DISPOSITION:
370       fprintf(log, "an Invalid Disposition");
371       break;
372 
373     case EXCEPTION_INVALID_HANDLE:
374       fprintf(log, "an Invalid Handle");
375       break;
376 
377     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
378       fprintf(log, "a Noncontinuable Exception");
379       break;
380 
381     case EXCEPTION_PRIV_INSTRUCTION:
382       fprintf(log, "a Privileged Instruction");
383       break;
384 
385     case EXCEPTION_SINGLE_STEP:
386       fprintf(log, "a Single Step");
387       break;
388 
389     case EXCEPTION_STACK_OVERFLOW:
390       fprintf(log, "a Stack Overflow");
391       break;
392 
393     case DBG_CONTROL_C:
394       fprintf(log, "a Control+C");
395       break;
396 
397     case DBG_CONTROL_BREAK:
398       fprintf(log, "a Control+Break");
399       break;
400 
401     case DBG_TERMINATE_THREAD:
402       fprintf(log, "a Terminate Thread");
403       break;
404 
405     case DBG_TERMINATE_PROCESS:
406       fprintf(log, "a Terminate Process");
407       break;
408 
409     case RPC_S_UNKNOWN_IF:
410       fprintf(log, "an Unknown Interface");
411       break;
412 
413     case RPC_S_SERVER_UNAVAILABLE:
414       fprintf(log, "a Server Unavailable");
415       break;
416 
417     default:
418       fprintf(log, "an Unknown [0x%lX] Exception", exception_record->ExceptionCode);
419       break;
420     }
421 
422   fprintf(log, " at location %08x", (int) exception_record->ExceptionAddress);
423   if ((hModule = (HMODULE) GetModuleBase((DWORD) exception_record->ExceptionAddress) && GetModuleFileName(hModule, szModule, sizeof(szModule))))
424     fprintf(log, " in module %s", szModule);
425 
426   // If the exception was an access violation, print out some additional information, to the error log and the debugger.
427   if(exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && exception_record->NumberParameters >= 2)
428     fprintf(log, " %s location %08x\n\n", exception_record->ExceptionInformation[0] ? "writing to" : "reading from", exception_record->ExceptionInformation[1]);
429 
430   DWORD pid = GetCurrentProcessId();
431   HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
432 
433   dump_registers(log, context_record);
434   unwind_stack(log, process, context_record);
435 
436   print_module_list(log);
437 
438   fprintf(log, "\nRegistry dump:\n\n");
439   dump_registry(log, HKEY_CURRENT_USER, "Software\\Workrave");
440 
441   fclose(log);
442 
443   if( GetEnvironmentVariableW && CreateFileW )
444   // >= WinNT
445     {
446       WCHAR *one =
447           L"Workrave has unexpectedly crashed. A crash log has been saved to:\n";
448 
449       WCHAR *two =
450           L"\nPlease file a bug report: http://issues.workrave.org/\n"
451           L"Thanks.";
452 
453       WCHAR *nomem =
454           L"Workrave is out of memory!";
455 
456       int size = wcslen( one ) + wcslen( p_wbuffer ) + wcslen( two ) + 1;
457 
458       WCHAR *message = (WCHAR *)calloc( size, sizeof( WCHAR ) );
459       if( !message )
460       // Low memory...
461         {
462           // % + % + null = 3 extra
463           size = wcslen( one ) + wcslen( env_var ) + wcslen( crashlog ) + wcslen( two ) + 3;
464           message = (WCHAR *)calloc( size, sizeof( WCHAR ) );
465           if( message )
466             {
467               snwprintf( message, size - 1, L"%ws%%%ws%%%ws%ws",
468                   one, env_var, crashlog, two );
469               message[ size - 1 ] = L'\0';
470             }
471            else
472            // No memory...
473               message = nomem;
474         }
475       else
476       // A buffer was allocated with enough memory to hold p_wbuffer
477         {
478           snwprintf( message, size - 1, L"%ws%ws%ws", one, p_wbuffer, two );
479           message[ size - 1 ] = L'\0';
480         }
481 
482       MessageBoxW( NULL, message, L"Exception", MB_OK );
483     }
484   else
485     {
486       snprintf(crash_text, 1023,
487         "Workrave has unexpectedly crashed. A crash log has been saved to "
488         "%s. Please mail this file to crashes@workrave.org or "
489         "file a bugreport at: http://issues.workrave.org/. "
490         "Thanks.", crash_log_name);
491       MessageBoxA(NULL, crash_text, "Exception", MB_OK);
492     }
493 
494   __except1;
495 
496   exit(1);
497 }
498 
499 
500 static
stack_walk(HANDLE process,LPSTACKFRAME stack_frame,PCONTEXT context_record)501 BOOL WINAPI stack_walk(HANDLE process, LPSTACKFRAME stack_frame, PCONTEXT context_record)
502 {
503   if (!stack_frame->Reserved[0])
504     {
505       stack_frame->Reserved[0] = 1;
506 
507       stack_frame->AddrPC.Mode = AddrModeFlat;
508       stack_frame->AddrPC.Offset = context_record->Eip;
509       stack_frame->AddrStack.Mode = AddrModeFlat;
510       stack_frame->AddrStack.Offset = context_record->Esp;
511       stack_frame->AddrFrame.Mode = AddrModeFlat;
512       stack_frame->AddrFrame.Offset = context_record->Ebp;
513 
514       stack_frame->AddrReturn.Mode = AddrModeFlat;
515       if (!ReadProcessMemory(process,
516                              (LPCVOID) (stack_frame->AddrFrame.Offset + sizeof(DWORD)),
517                              &stack_frame->AddrReturn.Offset, sizeof(DWORD), NULL))
518         return FALSE;
519     }
520   else
521     {
522       stack_frame->AddrPC.Offset = stack_frame->AddrReturn.Offset;
523 
524       if (!ReadProcessMemory(process, (LPCVOID) stack_frame->AddrFrame.Offset,
525                             &stack_frame->AddrFrame.Offset, sizeof(DWORD), NULL))
526         return FALSE;
527 
528       if (!ReadProcessMemory(process, (LPCVOID) (stack_frame->AddrFrame.Offset + sizeof(DWORD)),
529                              &stack_frame->AddrReturn.Offset, sizeof(DWORD), NULL))
530         return FALSE;
531     }
532 
533   ReadProcessMemory(process, (LPCVOID) (stack_frame->AddrFrame.Offset + 2*sizeof(DWORD)),
534                     stack_frame->Params, sizeof(stack_frame->Params), NULL);
535 
536   return TRUE;
537 }
538 
539 static void
unwind_stack(FILE * log,HANDLE process,PCONTEXT context)540 unwind_stack(FILE *log, HANDLE process, PCONTEXT context)
541 {
542   STACKFRAME          sf;
543 
544   fprintf(log, "Stack trace:\n\n");
545 
546   ZeroMemory(&sf,  sizeof(STACKFRAME));
547   sf.AddrPC.Offset    = context->Eip;
548   sf.AddrPC.Mode      = AddrModeFlat;
549   sf.AddrStack.Offset = context->Esp;
550   sf.AddrStack.Mode   = AddrModeFlat;
551   sf.AddrFrame.Offset = context->Ebp;
552   sf.AddrFrame.Mode   = AddrModeFlat;
553 
554   fprintf(log, "PC        Frame     Ret\n");
555 
556   while (TRUE)
557     {
558       if (!stack_walk(process, &sf, context))
559         break;
560 
561       if (sf.AddrFrame.Offset == 0)
562         break;
563 
564       fprintf(log, "%08X  %08X  %08X\n",
565               (int) sf.AddrPC.Offset,
566               (int) sf.AddrFrame.Offset,
567               (int) sf.AddrReturn.Offset);
568     }
569 }
570 
571 static void
print_module_info(FILE * log,HMODULE mod)572 print_module_info(FILE *log, HMODULE mod)
573 {
574   TCHAR buffer[MAX_PATH];
575   HANDLE file;
576   SYSTEMTIME file_time;
577   FILETIME write_time;
578 
579   GetModuleFileName(mod, buffer, MAX_PATH);
580 
581   file = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
582   if (file != INVALID_HANDLE_VALUE)
583     {
584       if (GetFileTime(file, NULL, NULL, &write_time))
585         {
586           FileTimeToSystemTime(&write_time, &file_time);
587         }
588       CloseHandle(file);
589     }
590 
591   fprintf(log, " %-20s handle: %p date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n",
592           buffer,
593           mod,
594           file_time.wYear,
595           file_time.wMonth,
596           file_time.wDay,
597           file_time.wHour,
598           file_time.wMinute,
599           file_time.wSecond
600           );
601 }
602 
603 void
print_module_list(FILE * log)604 print_module_list(FILE *log)
605 {
606   HMODULE lib;
607   BOOL (WINAPI *EnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD);
608 
609   EnumProcessModules = NULL;
610   lib = LoadLibrary("psapi.dll");
611   if (lib != NULL)
612     {
613       EnumProcessModules = GetProcAddress(lib, "EnumProcessModules");
614     }
615 
616   if (EnumProcessModules != NULL)
617     {
618       HMODULE modules[100];
619       DWORD needed;
620       BOOL res;
621       int count, i;
622 
623       HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
624       if (proc != NULL)
625         {
626           res = EnumProcessModules(proc, modules, sizeof(modules), &needed);
627           CloseHandle(proc);
628           if (res)
629             {
630               count = min(needed / sizeof(HMODULE), 100);
631 
632               for (i = 0; i != count; i++)
633                 {
634                   print_module_info(log, modules[i]);
635                 }
636               return;
637             }
638         }
639     }
640 
641   print_module_info(log, NULL);
642 }
643 
644 static void
dump_registers(FILE * log,PCONTEXT context)645 dump_registers(FILE *log, PCONTEXT context)
646 {
647   fprintf(log, "Registers:\n\n");
648 
649   if (context->ContextFlags & CONTEXT_INTEGER)
650     {
651       fprintf(log, "eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
652               context->Eax, context->Ebx, context->Ecx, context->Edx,
653               context->Esi, context->Edi);
654     }
655 
656   if (context->ContextFlags & CONTEXT_CONTROL)
657     {
658       fprintf(log, "eip=%08lx esp=%08lx ebp=%08lx iopl=%1lx %s %s %s %s %s %s %s %s %s %s\n",
659               context->Eip, context->Esp, context->Ebp,
660               (context->EFlags >> 12) & 3,  //  IOPL level value
661               context->EFlags & 0x00100000 ? "vip" : "   ", //  VIP (virtual interrupt pending)
662               context->EFlags & 0x00080000 ? "vif" : "   ", //  VIF (virtual interrupt flag)
663               context->EFlags & 0x00000800 ? "ov" : "nv", //  VIF (virtual interrupt flag)
664               context->EFlags & 0x00000400 ? "dn" : "up", //  OF (overflow flag)
665               context->EFlags & 0x00000200 ? "ei" : "di", //  IF (interrupt enable flag)
666               context->EFlags & 0x00000080 ? "ng" : "pl", //  SF (sign flag)
667               context->EFlags & 0x00000040 ? "zr" : "nz", //  ZF (zero flag)
668               context->EFlags & 0x00000010 ? "ac" : "na", //  AF (aux carry flag)
669               context->EFlags & 0x00000004 ? "po" : "pe", //  PF (parity flag)
670               context->EFlags & 0x00000001 ? "cy" : "nc"  //  CF (carry flag)
671               );
672     }
673 
674   if (context->ContextFlags & CONTEXT_SEGMENTS)
675     {
676       fprintf(log, "cs=%04lx  ss=%04lx  ds=%04lx  es=%04lx  fs=%04lx  gs=%04lx",
677               context->SegCs, context->SegSs, context->SegDs, context->SegEs,
678               context->SegFs, context->SegGs);
679 
680       if(context->ContextFlags & CONTEXT_CONTROL)
681         {
682           fprintf(log, "             efl=%08lx", context->EFlags);
683         }
684     }
685   else
686     {
687       if (context->ContextFlags & CONTEXT_CONTROL)
688         {
689           fprintf(log, "                                                                       efl=%08lx",
690                   context->EFlags);
691         }
692     }
693 
694   fprintf(log, "\n\n");
695 }
696 
697 static void
save_key(FILE * log,HKEY key,char * name)698 save_key(FILE *log, HKEY key, char *name)
699 {
700   DWORD i;
701   char keyname[512];
702   int keyname_len = strlen(keyname);
703 
704   fprintf(log, "key = %s\n", name);
705 
706   for (i = 0; ; i++)
707     {
708       char val[256];
709       DWORD val_size = sizeof(val);
710       BYTE data[0x4000];
711       DWORD data_size = sizeof(data);
712       DWORD type;
713 
714       LONG rc = RegEnumValue(key, i, val, &val_size, 0, &type, data, &data_size);
715 
716       if (rc != ERROR_SUCCESS)
717         break;
718 
719       if (val_size)
720         fprintf(log, "  value = %s\n", val);
721 
722       if (strcmp("password", val) == 0)
723         {
724           fprintf(log, "  string data = <hidden>\n");
725         }
726       else if (type == REG_SZ)
727         {
728           fprintf(log, "  string data = %s\n", data);
729         }
730       else if (type == REG_DWORD && data_size==4)
731         {
732           fprintf(log, "  dword data = %08lx\n", (long)data);
733         }
734       else
735         {
736           fprintf(log, "  hex data = [unsupported]\n");
737         }
738     }
739 
740   fprintf(log, "\n");
741 
742   strcpy(keyname, name);
743   strcat(keyname, "\\");
744   keyname_len = strlen(keyname);
745 
746   for (i = 0; ; i++)
747     {
748       HKEY subkey;
749       LONG rc = RegEnumKey(key, i, keyname + keyname_len,
750                            sizeof(keyname) - keyname_len);
751 
752       if (rc != ERROR_SUCCESS)
753         break;
754 
755       rc = RegOpenKey(key, keyname + keyname_len, &subkey);
756       if (rc == ERROR_SUCCESS)
757         {
758           dump_registry(log, subkey, keyname);
759           RegCloseKey(subkey);
760         }
761     }
762 }
763 
764 
765 static void
dump_registry(FILE * log,HKEY key,char * name)766 dump_registry(FILE *log, HKEY key, char *name)
767 {
768   (void) key;
769 
770   HKEY handle;
771   LONG rc = RegOpenKeyEx(HKEY_CURRENT_USER, name, 0, KEY_ALL_ACCESS, &handle);
772 
773   if (rc == ERROR_SUCCESS)
774     {
775       save_key(log, handle, name);
776       RegCloseKey(handle);
777     }
778 }
779 
780 
781