-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathProgram.cs
289 lines (249 loc) · 11.1 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
using System;
using System.IO;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace DumpReport
{
class Program
{
const int EXIT_SUCCESS = 0;
const int EXIT_FAILURE = 1;
static public string configFile = null;
static public string appDirectory = null;
static public bool is32bitDump = false; // True if the dump corresponds to a 32-bit process
static int exitCode = EXIT_SUCCESS;
static Config config = new Config(); // Stores the paramaters of the application
static Report report = new Report(config); // Outputs extracted data to an HTML file
static LogManager logManager = new LogManager(config, report); // Parses the debugger's output log
static int Main(string[] args)
{
try
{
WriteTitle();
appDirectory = new FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName;
configFile = Utils.GetAbsolutePath(Resources.configFile);
// If the user just requests help, show help and quit.
if (config.CheckHelp(args) == true)
return EXIT_SUCCESS;
// Read parameters from config file and command line
config.ReadConfigFile(configFile);
config.ReadCommandLine(args);
// Create the report file
report.Open(config.ReportFile);
// Basic check of the input parameters
config.CheckArguments();
WriteDebuggerVersion();
WriteConsole("Processing dump " + config.DumpFile);
WriteConsole("Checking dump bitness...", true);
// Find out dump bitness.
LaunchDebugger(Resources.dbgScriptInit, config.LogFile);
CheckDumpBitness();
// Execute main debugger script
WriteConsole("Creating log...", true);
LaunchDebugger(Resources.dbgScriptMain, config.LogFile, !config.QuietMode);
// Process debugger's output
WriteConsole("Reading log...", true);
logManager.ReadLog();
logManager.ParseLog();
logManager.CombineParserInfo();
// If the dump reveals an exception but the details are missing, try to find them
if (logManager.GetExceptionInfo() && logManager.NeedMoreExceptionInfo())
{
FindExceptionRecord(); // Execute a new script in order to retrieve the exception record
logManager.GetExceptionInfo(); // Check again with the new script output
}
// Write the extracted information to the report file
logManager.WriteReport();
}
catch (Exception ex)
{
ShowError(ex.Message);
}
if (report.IsOpen())
{
report.Close();
if (config.ReportShow && File.Exists(config.ReportFile))
LaunchBrowser(config.ReportFile);
}
WriteConsole("Finished.");
return exitCode; // Return success (0) or failure (1)
}
// Selects the most appropiate debugger to use depending on the current OS and the dump bitness
public static string GetDebuggerPath()
{
if (!Environment.Is64BitOperatingSystem)
return config.DbgExe32;
if (is32bitDump)
{
if (config.DbgExe32.Length > 0)
return config.DbgExe32;
return config.DbgExe64;
}
else
{
if (config.DbgExe64.Length > 0)
return config.DbgExe64;
return config.DbgExe32;
}
}
static async Task<bool> LaunchDebuggerAsync(Process process)
{
return await Task.Run(() =>
{
return process.WaitForExit(config.DbgTimeout * 60 * 1000); // Convert minutes to milliseconds
});
}
static string PreprocessScript(string script, string outFile, LogProgress progress)
{
// Insert the output path in the script
script = script.Replace("{LOG_FILE}", outFile);
// Set the proper intruction pointer register
script = script.Replace("{INSTRUCT_PTR}", is32bitDump ? "@eip" : "@rip");
// Adapt or remove the progress information
if (progress != null)
script = progress.PrepareScript(script, outFile, is32bitDump);
else
script = LogProgress.RemoveProgressMark(script);
return script;
}
// Launches the debugger, which automatically executes a script and stores the output into a file
public static void LaunchDebugger(string script, string outFile, bool showProgress = false)
{
LogProgress progress = showProgress ? new LogProgress() : null;
// Set the path of the temporary script file
string scriptFile = Path.Combine(Path.GetTempPath(), "WinDbgScript.txt");
// Replace special marks in the original script
script = PreprocessScript(script, outFile, progress);
// Remove old files
File.Delete(scriptFile);
File.Delete(outFile);
// Create the script file
using (StreamWriter stream = new StreamWriter(scriptFile))
stream.WriteLine(script);
// Start the debugger
string arguments = string.Format(@"-y ""{0};srv*{1}*http://msdl.microsoft.com/download/symbols"" -z ""{2}"" -c ""$$><{3};q""",
config.PdbFolder, config.SymbolCache, config.DumpFile, scriptFile);
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = GetDebuggerPath(),
Arguments = arguments,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden // WinDBG will only hide the main window
};
Process process = new Process();
process.StartInfo = psi;
if (!process.Start())
throw new Exception("The debugger could not be launched.");
Task<bool> task = LaunchDebuggerAsync(process);
while (!task.IsCompleted)
{
task.Wait(500);
if (showProgress)
progress.ShowLogProgress();
}
bool exited = task.Result;
File.Delete(scriptFile);
if (!exited)
{
process.Kill();
throw new Exception(String.Format("Execution has been cancelled after {0} minutes.", config.DbgTimeout));
}
if (process.ExitCode != 0)
throw new Exception("The debugger did not finish properly.");
if (showProgress)
progress.DeleteProgressFile();
// Check that the output log has been generated
if (!File.Exists(outFile))
throw new Exception("The debugger did not generate any output.");
}
// Opens an html file with the default browser
public static void LaunchBrowser(string htmlFile)
{
Process.Start(htmlFile);
}
// Determines whether the dump corresponds to a 32 or 64-bit process, by reading the
// output of a script previously executed by the debugger
public static void CheckDumpBitness()
{
bool x86Found = false;
bool wow64Found = false;
// Read the output file generated by the debugger
using (StreamReader file = new StreamReader(config.LogFile, Encoding.Unicode))
{
string line;
while ((line = file.ReadLine()) != null)
{
if (line.Contains("WOW64 found"))
wow64Found = true;
else if (line.Contains("Effective machine") && line.Contains("x86"))
x86Found = true;
}
is32bitDump = (wow64Found || x86Found);
}
if (is32bitDump && GetDebuggerPath() == config.DbgExe64)
logManager.notes.Add("32-bit dump processed with a 64-bit debugger.");
else if (!is32bitDump && GetDebuggerPath() == config.DbgExe32)
logManager.notes.Add("64-bit dump processed with a 32-bit debugger.");
if (wow64Found)
logManager.notes.Add("64-bit dumps of 32-bit processes may show inaccurate or incomplete call stack traces.");
}
// Tries to obtain the proper exception record by using auxiliary debugger scripts.
public static void FindExceptionRecord()
{
string exrLogFile = config.LogFile;
exrLogFile = Path.ChangeExtension(exrLogFile, ".exr.log"); // Store the output of the exception record script in a separate file
File.Delete(exrLogFile); // Delete previous logs
WriteConsole("Getting exception record...", true);
string script = logManager.GetExceptionRecordScript();
if (script != null)
LaunchDebugger(script, exrLogFile);
if (File.Exists(exrLogFile))
{
logManager.ParseExceptionRecord(exrLogFile);
if (config.LogClean)
File.Delete(exrLogFile);
}
}
public static void ShowError(string msg)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\n" + msg);
Console.ResetColor();
report.WriteError(msg);
exitCode = EXIT_FAILURE;
}
public static void WriteTitle()
{
if (config.QuietMode) return;
Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
Version version = assembly.GetName().Version;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(String.Format("{0} {1}.{2}", Assembly.GetCallingAssembly().GetName().Name,
version.Major, version.Minor));
Console.ResetColor();
}
public static void WriteDebuggerVersion()
{
if (config.QuietMode) return;
string debuggerPath = GetDebuggerPath();
var versionInfo = FileVersionInfo.GetVersionInfo(debuggerPath);
string version = versionInfo.FileVersion;
Console.WriteLine("Using {0} version {1}", Path.GetFileName(debuggerPath).ToLower(), version);
}
public static void WriteConsole(string msg, bool sameLine = false)
{
if (config.QuietMode) return;
if (sameLine)
{
string blank = String.Empty;
blank = blank.PadLeft(Console.WindowWidth - 1, ' ');
Console.Write("\r" + blank); // Clean the line before writing
Console.Write("\r" + msg);
}
else
Console.WriteLine(msg);
}
}
}