Skip to content

Commit f04cf25

Browse files
committed
Move all executable handling to Executable
1 parent eb8b9da commit f04cf25

File tree

3 files changed

+215
-290
lines changed

3 files changed

+215
-290
lines changed

BinaryObjectScanner/FileType/Executable.cs

Lines changed: 133 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using BinaryObjectScanner.Data;
56
using BinaryObjectScanner.Interfaces;
67
using SabreTools.IO.Extensions;
8+
using SabreTools.Serialization.Interfaces;
79
using SabreTools.Serialization.Wrappers;
810

911
namespace BinaryObjectScanner.FileType
@@ -44,43 +46,94 @@ public class Executable : IDetectable
4446
/// <inheritdoc/>
4547
public string? Detect(Stream stream, string file, bool includeDebug)
4648
{
47-
// Try to create a wrapper for the proper executable type
48-
var wrapper = WrapperFactory.CreateExecutableWrapper(stream);
49-
if (wrapper == null)
49+
// Get all non-nested protections
50+
var protections = DetectDict(stream, file, scanner: null, includeDebug);
51+
if (protections.Count == 0)
5052
return null;
5153

5254
// Create the internal list
53-
var protections = new List<string>();
55+
var protectionList = new List<string>();
56+
foreach (string key in protections.Keys)
57+
{
58+
protectionList.AddRange(protections[key]);
59+
}
60+
61+
return string.Join(";", [.. protections]);
62+
}
63+
64+
/// <inheritdoc cref="IDetectable.Detect(Stream, string, bool)"/>
65+
/// <remarks>
66+
/// Ideally, we wouldn't need to circumvent the proper handling of file types just for Executable,
67+
/// but due to the complexity of scanning, this is not currently possible.
68+
/// </remarks>
69+
public ProtectionDictionary DetectDict(Stream stream, string file, Scanner? scanner, bool includeDebug)
70+
{
71+
// Create the output dictionary
72+
var protections = new ProtectionDictionary();
73+
74+
// Try to create a wrapper for the proper executable type
75+
IWrapper? wrapper;
76+
try
77+
{
78+
wrapper = WrapperFactory.CreateExecutableWrapper(stream);
79+
if (wrapper == null)
80+
return protections;
81+
}
82+
catch (Exception ex)
83+
{
84+
if (includeDebug) Console.WriteLine(ex);
85+
return protections;
86+
}
5487

5588
// Only use generic content checks if we're in debug mode
5689
if (includeDebug)
5790
{
58-
var contentProtections = RunContentChecks(file, stream, includeDebug);
59-
protections.AddRange(contentProtections.Values);
91+
var subProtections = RunContentChecks(file, stream, includeDebug);
92+
protections.Append(file, subProtections.Values);
6093
}
6194

6295
if (wrapper is MSDOS mz)
6396
{
97+
// Standard checks
6498
var subProtections = RunExecutableChecks(file, mz, StaticChecks.MSDOSExecutableCheckClasses, includeDebug);
65-
protections.AddRange(subProtections.Values);
99+
protections.Append(file, subProtections.Values);
100+
101+
// Extractable checks
102+
var extractedProtections = HandleExtractableProtections(file, mz, subProtections.Keys, scanner, includeDebug);
103+
protections.Append(extractedProtections);
66104
}
67105
else if (wrapper is LinearExecutable lex)
68106
{
107+
// Standard checks
69108
var subProtections = RunExecutableChecks(file, lex, StaticChecks.LinearExecutableCheckClasses, includeDebug);
70-
protections.AddRange(subProtections.Values);
109+
protections.Append(file, subProtections.Values);
110+
111+
// Extractable checks
112+
var extractedProtections = HandleExtractableProtections(file, lex, subProtections.Keys, scanner, includeDebug);
113+
protections.Append(extractedProtections);
71114
}
72115
else if (wrapper is NewExecutable nex)
73116
{
117+
// Standard checks
74118
var subProtections = RunExecutableChecks(file, nex, StaticChecks.NewExecutableCheckClasses, includeDebug);
75-
protections.AddRange(subProtections.Values);
119+
protections.Append(file, subProtections.Values);
120+
121+
// Extractable checks
122+
var extractedProtections = HandleExtractableProtections(file, nex, subProtections.Keys, scanner, includeDebug);
123+
protections.Append(extractedProtections);
76124
}
77125
else if (wrapper is PortableExecutable pex)
78126
{
127+
// Standard checks
79128
var subProtections = RunExecutableChecks(file, pex, StaticChecks.PortableExecutableCheckClasses, includeDebug);
80-
protections.AddRange(subProtections.Values);
129+
protections.Append(file, subProtections.Values);
130+
131+
// Extractable checks
132+
var extractedProtections = HandleExtractableProtections(file, pex, subProtections.Keys, scanner, includeDebug);
133+
protections.Append(extractedProtections);
81134
}
82135

83-
return string.Join(";", [.. protections]);
136+
return protections;
84137
}
85138

86139
#region Check Runners
@@ -145,6 +198,7 @@ public IDictionary<IContentCheck, string> RunContentChecks(string? file, Stream
145198
/// <param name="file">Name of the source file of the executable, for tracking</param>
146199
/// <param name="exe">Executable to scan</param>
147200
/// <param name="checks">Set of checks to use</param>
201+
/// <param name="scanner">Scanner for handling recursive protections</param>
148202
/// <param name="includeDebug">True to include debug data, false otherwise</param>
149203
/// <returns>Set of protections in file, empty on error</returns>
150204
public IDictionary<U, string> RunExecutableChecks<T, U>(string file, T exe, List<U> checks, bool includeDebug)
@@ -176,6 +230,74 @@ public IDictionary<U, string> RunExecutableChecks<T, U>(string file, T exe, List
176230
return protections;
177231
}
178232

233+
/// <summary>
234+
/// Handle extractable protections, such as executable packers
235+
/// </summary>
236+
/// <param name="file">Name of the source file of the stream, for tracking</param>
237+
/// <param name="exe">Executable to scan the contents of</param>
238+
/// <param name="checks">Set of classes returned from Exectuable scans</param>
239+
/// <param name="scanner">Scanner for handling recursive protections</param>
240+
/// <param name="includeDebug">True to include debug data, false otherwise</param>
241+
/// <returns>Set of protections found from extraction, empty on error</returns>
242+
private ProtectionDictionary HandleExtractableProtections<T, U>(string file, T exe, IEnumerable<U> checks, Scanner? scanner, bool includeDebug)
243+
where T : WrapperBase
244+
where U : IExecutableCheck<T>
245+
{
246+
// Create the output dictionary
247+
var protections = new ProtectionDictionary();
248+
249+
// If we have an invalid set of classes
250+
if (checks == null || !checks.Any())
251+
return protections;
252+
253+
// If we have any extractable packers
254+
var extractables = checks
255+
.Where(c => c is IExtractableExecutable<T>)
256+
.Select(c => c as IExtractableExecutable<T>);
257+
extractables.IterateWithAction(extractable =>
258+
{
259+
// If we have an invalid extractable somehow
260+
if (extractable == null)
261+
return;
262+
263+
// If the extractable file itself fails
264+
try
265+
{
266+
// Extract and get the output path
267+
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
268+
bool extracted = extractable.Extract(file, exe, tempPath, includeDebug);
269+
270+
// Collect and format all found protections
271+
ProtectionDictionary? subProtections = null;
272+
if (extracted)
273+
subProtections = scanner?.GetProtections(tempPath);
274+
275+
// If temp directory cleanup fails
276+
try
277+
{
278+
if (Directory.Exists(tempPath))
279+
Directory.Delete(tempPath, true);
280+
}
281+
catch (Exception ex)
282+
{
283+
if (includeDebug) Console.WriteLine(ex);
284+
}
285+
286+
// Prepare the returned protections
287+
subProtections?.StripFromKeys(tempPath);
288+
subProtections?.PrependToKeys(file);
289+
if (subProtections != null)
290+
protections.Append(subProtections);
291+
}
292+
catch (Exception ex)
293+
{
294+
if (includeDebug) Console.WriteLine(ex);
295+
}
296+
});
297+
298+
return protections;
299+
}
300+
179301
#endregion
180302

181303
#region Helpers

BinaryObjectScanner/Handler.cs

Lines changed: 0 additions & 126 deletions
This file was deleted.

0 commit comments

Comments
 (0)