Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ExceptionHandler.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2005. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: ExceptionHandler.cpp
7  AUTHOR: John DiCamillo
8 
9 */
10 
11 
12 #include <windows.h>
13 #include <imagehlp.h>
14 
15 extern void Print(const char* fmt, ...);
16 
17 // +--------------------------------------------------------------------+
18 
20 {
21 public:
24 
25 private:
26  static LPTOP_LEVEL_EXCEPTION_FILTER old_filter;
27 
28  static LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* info);
29 
30  static void PrintReport(EXCEPTION_POINTERS* info);
31 
32  // Helper functions
33  static const char* GetExceptionString(DWORD dwCode);
34  static BOOL GetLogicalAddress(VOID* addr, char* module, int len,
35  DWORD& section, DWORD& offset);
36 
37  static BOOL InitImageHelp();
38  static void ImageStackTrace(CONTEXT* context);
39  static void IntelStackTrace(CONTEXT* context);
40 
41 
42  // Make typedefs for some IMAGEHLP.DLL functions so that we can use them
43  // with GetProcAddress
44  typedef BOOL (__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
45  typedef BOOL (__stdcall * SYMCLEANUPPROC)(HANDLE);
46 
47  typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD);
48  typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD);
49  typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
50 
51  typedef BOOL (__stdcall * STACKWALKPROC)(DWORD,
52  HANDLE,
53  HANDLE,
54  LPSTACKFRAME,
55  LPVOID,
56  PREAD_PROCESS_MEMORY_ROUTINE,
57  PFUNCTION_TABLE_ACCESS_ROUTINE,
58  PGET_MODULE_BASE_ROUTINE,
59  PTRANSLATE_ADDRESS_ROUTINE);
60 
61  static SYMINITIALIZEPROC SymInitialize;
62  static SYMCLEANUPPROC SymCleanup;
63  static STACKWALKPROC StackTrace;
64  static SYMFUNCTIONTABLEACCESSPROC SymFunctionTableAccess;
65  static SYMGETMODULEBASEPROC SymGetModuleBase;
66  static SYMGETSYMFROMADDRPROC SymGetSymFromAddr;
67 };
68 
69 // +--------------------------------------------------------------------+
70 
71 LPTOP_LEVEL_EXCEPTION_FILTER ExceptionHandler::old_filter = 0;
72 
73 ExceptionHandler::SYMINITIALIZEPROC ExceptionHandler::SymInitialize = 0;
74 ExceptionHandler::SYMCLEANUPPROC ExceptionHandler::SymCleanup = 0;
75 ExceptionHandler::STACKWALKPROC ExceptionHandler::StackTrace = 0;
76 
77 ExceptionHandler::SYMFUNCTIONTABLEACCESSPROC
78 ExceptionHandler::SymFunctionTableAccess = 0;
79 
80 ExceptionHandler::SYMGETMODULEBASEPROC
81 ExceptionHandler::SymGetModuleBase = 0;
82 
83 ExceptionHandler::SYMGETSYMFROMADDRPROC
84 ExceptionHandler::SymGetSymFromAddr = 0;
85 
87 
88 
89 // +--------------------------------------------------------------------+
90 
92 {
93  old_filter = SetUnhandledExceptionFilter(ExceptionFilter);
94 }
95 
97 {
98  SetUnhandledExceptionFilter(old_filter);
99 }
100 
101 // +--------------------------------------------------------------------+
102 
103 static bool in_filter = false;
104 
105 LONG WINAPI
106 ExceptionHandler::ExceptionFilter(EXCEPTION_POINTERS* info)
107 {
108  if (in_filter) {
109  Print("\n\n*********************************************\n");
110  Print("SECOND EXCEPTION CAUGHT: TERMINATING.\n");
111  Print("*********************************************\n");
112  }
113 
114  else {
115  in_filter = true;
116  PrintReport(info);
117  in_filter = false;
118  }
119 
120  if (old_filter)
121  return old_filter(info);
122  else
123  return EXCEPTION_CONTINUE_SEARCH;
124 }
125 
126 // +--------------------------------------------------------------------+
127 
128 void
129 ExceptionHandler::PrintReport(EXCEPTION_POINTERS* info)
130 {
131  EXCEPTION_RECORD* record = info->ExceptionRecord;
132  CONTEXT* context = info->ContextRecord;
133  DWORD code = record->ExceptionCode;
134 
135  Print("\n*********************************************\n");
136  Print("FATAL EXCEPTION:\n");
137 
138  Print("\nRegisters:\n");
139  Print("EAX: %08x\n", context->Eax);
140  Print("EBX: %08x\n", context->Ebx);
141  Print("ECX: %08x\n", context->Ecx);
142  Print("EDX: %08x\n", context->Edx);
143  Print("EDI: %08x\n", context->Edi);
144  Print("ESI: %08x\n", context->Esi);
145  Print("EBP: %08x\n", context->Ebp);
146  Print("\n");
147  Print("CS:EIP: %04x:%08x\n", context->SegCs, context->Eip);
148  Print("SS:ESP: %04x:%08x\n", context->SegSs, context->Esp);
149  Print("DS: %04x\n", context->SegDs);
150  Print("ES: %04x\n", context->SegEs);
151  Print("FS: %04x\n", context->SegFs);
152  Print("GS: %04x\n", context->SegGs);
153  Print("Flags: %08x\n", context->EFlags );
154  Print("\n");
155 
156  Print("Exception Code: %08x %s\n",code, GetExceptionString(code));
157  Print("Exception Addr: %08x \n", record->ExceptionAddress);
158 
159  if (code == EXCEPTION_ACCESS_VIOLATION && record->NumberParameters >= 2) {
160  if (record->ExceptionInformation[0])
161  Print(" Program attempted to WRITE to address 0x%08x\n", record->ExceptionInformation[1]);
162  else
163  Print(" Program attempted to READ from address 0x%08x\n", record->ExceptionInformation[1]);
164  }
165 
166  if (InitImageHelp()) {
167  ImageStackTrace(context);
168  SymCleanup(GetCurrentProcess());
169  }
170  else {
171  IntelStackTrace(context);
172  }
173 
174  Print("\n*********************************************\nPROGRAM TERMINATED.\n");
175 }
176 
177 // +--------------------------------------------------------------------+
178 
179 const char*
180 ExceptionHandler::GetExceptionString(DWORD code)
181 {
182 #define EXCEPTION( x ) case EXCEPTION_##x: return #x;
183 
184  switch (code) {
185  EXCEPTION( ACCESS_VIOLATION )
186  EXCEPTION( DATATYPE_MISALIGNMENT )
187  EXCEPTION( BREAKPOINT )
188  EXCEPTION( SINGLE_STEP )
189  EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
190  EXCEPTION( FLT_DENORMAL_OPERAND )
191  EXCEPTION( FLT_DIVIDE_BY_ZERO )
192  EXCEPTION( FLT_INEXACT_RESULT )
193  EXCEPTION( FLT_INVALID_OPERATION )
194  EXCEPTION( FLT_OVERFLOW )
195  EXCEPTION( FLT_STACK_CHECK )
196  EXCEPTION( FLT_UNDERFLOW )
197  EXCEPTION( INT_DIVIDE_BY_ZERO )
198  EXCEPTION( INT_OVERFLOW )
199  EXCEPTION( PRIV_INSTRUCTION )
200  EXCEPTION( IN_PAGE_ERROR )
201  EXCEPTION( ILLEGAL_INSTRUCTION )
202  EXCEPTION( NONCONTINUABLE_EXCEPTION )
203  EXCEPTION( STACK_OVERFLOW )
204  EXCEPTION( INVALID_DISPOSITION )
205  EXCEPTION( GUARD_PAGE )
206  EXCEPTION( INVALID_HANDLE )
207  }
208 
209  static char buffer[512] = { 0 };
210 
211  FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
212  GetModuleHandle("NTDLL.DLL"),
213  code, 0, buffer, sizeof(buffer), 0 );
214 
215  return buffer;
216 }
217 
218 // +--------------------------------------------------------------------+
219 
220 BOOL
221 ExceptionHandler::GetLogicalAddress(void* addr, char* mod_name, int len, DWORD& section, DWORD& offset)
222 {
223  MEMORY_BASIC_INFORMATION mbi;
224 
225  if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
226  return FALSE;
227 
228  DWORD hMod = (DWORD)mbi.AllocationBase;
229 
230  if (!GetModuleFileName((HMODULE)hMod, mod_name, len))
231  return FALSE;
232 
233  PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod;
234  PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + pDosHdr->e_lfanew);
235  PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr );
236 
237  DWORD rva = (DWORD)addr - hMod; // RVA is offset from module load address
238 
239  // Iterate through the section table, looking for the one that encompasses
240  // the linear address.
241  for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++ ) {
242  DWORD sectionStart = pSection->VirtualAddress;
243  DWORD sectionEnd = sectionStart
244  + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
245 
246  // Is the address in this section???
247  if ((rva >= sectionStart) && (rva <= sectionEnd)) {
248  // Yes, address is in the section. Calculate section and offset,
249  // and store in the "section" & "offset" params, which were
250  // passed by reference.
251  section = i+1;
252  offset = rva - sectionStart;
253  return TRUE;
254  }
255  }
256 
257  return FALSE; // Should never get here!
258 }
259 
260 // +--------------------------------------------------------------------+
261 
262 void
263 ExceptionHandler::IntelStackTrace(CONTEXT* context)
264 {
265  Print("\nStack Trace (Intel):\n");
266  Print("Address Frame Logical addr Module\n");
267 
268  DWORD pc = context->Eip;
269  DWORD* pFrame;
270  DWORD* pPrevFrame;
271 
272  pFrame = (DWORD*)context->Ebp;
273 
274  do {
275  char mod_name[256] = { 0 };
276  DWORD section = 0, offset = 0;
277 
278  GetLogicalAddress((void*)pc, mod_name, 256, section, offset);
279 
280  Print("%08X %08X %04X:%08X %s\n",
281  pc, pFrame, section, offset, mod_name);
282 
283  pc = pFrame[1];
284  pPrevFrame = pFrame;
285  pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack
286 
287  if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a
288  break; // DWORD boundary. Bail if not so.
289 
290  if (pFrame <= pPrevFrame)
291  break;
292 
293  // Can two DWORDs be read from the supposed frame address?
294  if (IsBadWritePtr(pFrame, sizeof(PVOID)*2))
295  break;
296 
297  }
298  while ( 1 );
299 }
300 
301 // +--------------------------------------------------------------------+
302 
303 void ExceptionHandler::ImageStackTrace(CONTEXT* context)
304 {
305  Print("\nStack Trace (Symbolic):\n");
306  Print("Address Frame\n");
307 
308  // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
309  STACKFRAME sf;
310  memset(&sf, 0, sizeof(sf));
311 
312  // Initialize the STACKFRAME structure for the first call. This is only
313  // necessary for Intel CPUs, and isn't mentioned in the documentation.
314  sf.AddrPC.Offset = context->Eip;
315  sf.AddrPC.Mode = AddrModeFlat;
316  sf.AddrStack.Offset = context->Esp;
317  sf.AddrStack.Mode = AddrModeFlat;
318  sf.AddrFrame.Offset = context->Ebp;
319  sf.AddrFrame.Mode = AddrModeFlat;
320 
321  while ( 1 ) {
322  if (!StackTrace( IMAGE_FILE_MACHINE_I386,
323  GetCurrentProcess(),
324  GetCurrentThread(),
325  &sf,
326  context,
327  0,
328  SymFunctionTableAccess,
329  SymGetModuleBase,
330  0))
331  break;
332 
333  if (sf.AddrFrame.Offset == 0) // Basic sanity check to make sure
334  break; // the frame is OK. Bail if not.
335 
336  Print("%08x %08x ", sf.AddrPC.Offset, sf.AddrFrame.Offset);
337 
338  // IMAGEHLP is wacky, and requires you to pass in a pointer to an
339  // IMAGEHLP_SYMBOL structure. The problem is that this structure is
340  // variable length. That is, you determine how big the structure is
341  // at runtime. This means that you can't use sizeof(struct).
342  // So...make a buffer that's big enough, and make a pointer
343  // to the buffer. We also need to initialize not one, but TWO
344  // members of the structure before it can be used.
345 
346  BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
347  PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
348  pSymbol->SizeOfStruct = sizeof(symbolBuffer);
349  pSymbol->MaxNameLength = 512;
350 
351  DWORD symDisplacement = 0; // Displacement of the input address,
352  // relative to the start of the symbol
353 
354  if (SymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset,
355  &symDisplacement, pSymbol)) {
356  Print("%-40s [%04X]\n", pSymbol->Name, symDisplacement);
357  }
358  else {
359  char mod_name[256] = { 0 };
360  DWORD section = 0, offset = 0;
361 
362  GetLogicalAddress((PVOID)sf.AddrPC.Offset,
363  mod_name, 256, section, offset );
364 
365  Print("%04X:%08X %s\n", section, offset, mod_name);
366  }
367  }
368 }
369 
370 // +--------------------------------------------------------------------+
371 
372 BOOL
373 ExceptionHandler::InitImageHelp()
374 {
375  Print("\n");
376 
377  HMODULE h = LoadLibrary("IMAGEHLP.DLL");
378  if (!h) {
379  Print("--- could not load IMAGEHLP.DLL (%08x) ---\n", GetLastError());
380  return FALSE;
381  }
382 
383  SymInitialize = (SYMINITIALIZEPROC) GetProcAddress(h, "SymInitialize");
384  if (!SymInitialize) {
385  Print("--- could not find SymInitialize ---\n");
386  return FALSE;
387  }
388 
389  SymCleanup = (SYMCLEANUPPROC) GetProcAddress(h, "SymCleanup");
390  if (!SymCleanup) {
391  Print("--- could not find SymCleanup ---\n");
392  return FALSE;
393  }
394 
395  StackTrace = (STACKWALKPROC) GetProcAddress(h, "StackWalk");
396  if (!StackTrace) {
397  Print("--- could not find StackWalk ---\n");
398  return FALSE;
399  }
400 
401  SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)
402  GetProcAddress(h, "SymFunctionTableAccess");
403 
404  if (!SymFunctionTableAccess) {
405  Print("--- could not find SymFunctionTableAccess ---\n");
406  return FALSE;
407  }
408 
409  SymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(h, "SymGetModuleBase");
410  if (!SymGetModuleBase) {
411  Print("--- could not find SymGetModuleBase ---\n");
412  return FALSE;
413  }
414 
415  SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(h, "SymGetSymFromAddr");
416  if (!SymGetSymFromAddr) {
417  Print("--- could not find SymGetSymFromAddr ---\n");
418  return FALSE;
419  }
420 
421  if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) {
422  Print("--- could not Initialize IMAGEHLP.DLL (%08x) ---\n", GetLastError());
423  return FALSE;
424  }
425 
426  Print("Loaded IMAGEHLP.DLL\n");
427  return TRUE;
428 }
429