-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathArchiveExtractor.cs
More file actions
317 lines (298 loc) · 15.6 KB
/
ArchiveExtractor.cs
File metadata and controls
317 lines (298 loc) · 15.6 KB
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// You must cut down the mightiest tree in the forest with a herring!
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Common;
using Microsoft.Win32;
using ProcessorEmulator.Tools;
// EVERYONE HAS A WATER BUFFALO
// YOURS IS FAST, BUT MINE IS SLOW
// WHERE WE GET THEM, I DON'T KNOW
// SPILLED SOME LIMA BEANS ON THE FLOOR
// FOR WE HAVE A WATER BUFFALO
// STOP STOP STOP WHAT DO YOU THINK YOU'RE DOING
// YOU CAN'T JUST PUT A SONG IN THE MIDDLE OF A CODE FILE
// IT'S NOT A SONG, IT'S A CARL CONTAINMENT PROTOCOL
// YOU'RE NOT SUPPOSED TO HAVE A CARL IN THE MIDDLE OF A CODE FILE
// IT'S NOT A CARL, IT'S A WATER BUFFALO
//Oh, I see, so you have a water buffalo in the middle of a code file
// YES, IT'S A WATER BUFFALO, NOT A CARL
// AND IT'S A CONTAINMENT PROTOCOL, NOT A SONG
// I DON'T CARE, YOU CAN'T JUST PUT A WATER BUFFALO IN THE MIDDLE OF A CODE FILE
// wE'RE NOT PUTTING A WATER BUFFALO IN THE MIDDLE OF A CODE FILE
// wE'RE GONNA GET NASTY COMMIT MESSAGES SAYING WHERES my WATER BUFFALO WHERES THE CARL WHERES THE CONTAINMENT PROTOCOL?
// AND ARE YOU PREPARED FOR THAT?
//sTOP BEING SO SILLY, THIS IS A SERIOUS PROTOCOL
// I don't think you are.
// THis has been a public service announcement from the Carl Containment Protocol team.
// tune in next time for more children's songs and spontaneously imploding code..
// Evveryone has a cotton candy machine
// Your's is pink, but mine is blue
// Where we get them, I don't know
// /AAAhhhh SHaaadddudp!
namespace ProcessorEmulator
{
public static class ArchiveExtractor
{
// Central repository of all known signatures for carving and analysis
private static Dictionary<string, byte[]> GetSignatures()
{
return new Dictionary<string, byte[]>
{
["kernel"] = new byte[]{0x1F,0x8B},
["elf"] = new byte[]{0x7F,(byte)'E',(byte)'L',(byte)'F'},
["uboot"] = new byte[]{0x27,0x05,0x19,0x56},
["squashfs"] = System.Text.Encoding.ASCII.GetBytes("hsqs"),
["jffs2"] = new byte[]{0xEF,0x53},
["lzma"] = new byte[]{0x5D,0x00,0x00},
["tar"] = System.Text.Encoding.ASCII.GetBytes("ustar"),
["zip"] = new byte[]{0x50,0x4B,0x03,0x04},
["jpeg"] = new byte[]{0xFF,0xD8,0xFF},
["png"] = new byte[]{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A},
["xz"] = new byte[]{0xFD,(byte)'7',(byte)'z',(byte)'X',(byte)'Z',0x00},
["lz4"] = new byte[]{0x04,0x22,0x4D,0x18},
["bzip2"] = new byte[]{0x42,0x5A,0x68},
["rar"] = new byte[]{0x52,0x61,0x72,0x21,0x1A,0x07,0x00},
["zstd"] = new byte[]{0x28,0xB5,0x2F,0xFD},
["lzop"] = new byte[]{0x89,0x4C,0x5A,0x4F,0x00,0x0D,0x0A,0x1A,0x0A},
["7z"] = new byte[]{0x37,0x7A,0xBC,0xAF,0x27,0x1C},
["exe"] = new byte[]{0x4D,0x5A},
["pdf"] = System.Text.Encoding.ASCII.GetBytes("%PDF"),
["bmp"] = new byte[]{0x42,0x4D},
["macho32"] = new byte[]{0xCE,0xFA,0xED,0xFE},
["macho64"] = new byte[]{0xCF,0xFA,0xED,0xFE},
["iso9660"] = System.Text.Encoding.ASCII.GetBytes("CD001"),
["cab"] = System.Text.Encoding.ASCII.GetBytes("MSCF"),
["trx"] = System.Text.Encoding.ASCII.GetBytes("HDR0"), // Broadcom TRX container
["xmi"] = System.Text.Encoding.ASCII.GetBytes("XMI\0"), // Mediaroom XMI package header
// Additional kernel and filesystem-specific headers
["bzImage"] = System.Text.Encoding.ASCII.GetBytes("HdrB"), // Linux bzImage header
["yaffs"] = System.Text.Encoding.ASCII.GetBytes("Yaffs") // YAFFS filesystem superblock
};
}
/// <summary>
/// Extracts an archive or raw disk image to the specified directory.
/// For .bin images, splits MBR partitions and extracts firmware sections by signature.
/// Otherwise invokes 7z.
/// </summary>
public static void ExtractArchive(string archivePath, string outputDir)
{
Directory.CreateDirectory(outputDir);
var ext = Path.GetExtension(archivePath);
// For raw binaries, use binwalk or fallback carving
if (string.IsNullOrEmpty(ext) || ext.Equals(".bin", StringComparison.OrdinalIgnoreCase))
{
try
{
var binwalk = new ProcessStartInfo("binwalk", $"-eM \"{archivePath}\" -C \"{outputDir}\"")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var bw = Process.Start(binwalk);
bw.WaitForExit();
Console.WriteLine(bw.StandardOutput.ReadToEnd());
Console.Error.WriteLine(bw.StandardError.ReadToEnd());
return;
}
catch
{
// binwalk not available, fallback to partition and signature carving
}
ExtractPartitions(archivePath, outputDir);
ExtractFirmwareSections(archivePath, outputDir);
return;
}
// Try SharpCompress for common archive formats
try
{
using var archive = ArchiveFactory.Open(archivePath);
var options = new ExtractionOptions { ExtractFullPath = true, Overwrite = true };
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
entry.WriteToDirectory(outputDir, options);
}
return;
}
catch
{
// SharpCompress failed or unsupported format, fallback to 7-Zip
}
// Fallback to 7z
var exe7z = Resolve7zExecutable();
if (string.IsNullOrEmpty(exe7z))
throw new InvalidOperationException("7z.exe not found. Please install 7-Zip.");
var psi7z = new ProcessStartInfo(exe7z, $"x -y -o\"{outputDir}\" \"{archivePath}\"")
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
using var p7 = Process.Start(psi7z);
p7.WaitForExit();
Sanitize(outputDir);
}
public static void ExtractAndAnalyze(string archivePath, string outputDir)
{
ExtractArchive(archivePath, outputDir);
FirmwareAnalyzer.AnalyzeFirmwareArchive(archivePath, outputDir);
}
/// <summary>
/// Analyzes a file buffer for known signatures and reports offsets without extracting.
/// </summary>
public static void AnalyzeArchive(string archivePath)
{
var buf = File.ReadAllBytes(archivePath);
var sigs = GetSignatures();
Console.WriteLine($"Analyzing {archivePath} for known signatures...");
foreach (var kv in sigs)
{
var name = kv.Key;
var patt = kv.Value;
int pos = 0;
bool foundAny = false;
while (pos < buf.Length)
{
int off = FindPattern(buf, patt, pos);
if (off < 0) break;
Console.WriteLine($" Found '{name}' at 0x{off:X}");
foundAny = true;
pos = off + 1;
}
if (!foundAny)
Console.WriteLine($" No '{name}' signatures found.");
}
}
//suppose that little things behave very differently than anything big
//nothing's really as it seems , its so wonderfully different than anything big
// the world is a dynmic mess of jiggling things its hard to believe
public static void ExtractPartitions(string img, string outDir)
{
var buf = File.ReadAllBytes(img);
if (buf.Length < 512 || buf[510] != 0x55 || buf[511] != 0xAA)
return;
for (int i = 0; i < 4; i++)
{
int off = 0x1BE + i * 16;
byte type = buf[off + 4];
uint lba = BitConverter.ToUInt32(buf, off + 8);
uint sec = BitConverter.ToUInt32(buf, off + 12);
if (type == 0 || sec == 0) continue;
long start = (long)lba * 512;
long length = (long)sec * 512;
if (start + length > buf.LongLength) length = buf.LongLength - start;
string partFile = Path.Combine(outDir, $"part{i + 1}_type{type:X2}.bin");
File.WriteAllBytes(partFile, buf.Skip((int)start).Take((int)length).ToArray());
}
}
public static void ExtractFirmwareSections(string img, string outDir)
{
var buf = File.ReadAllBytes(img);
var sigs = new Dictionary<string, byte[]>
{
["kernel"] = new byte[]{0x1F,0x8B}, // GZIP kernel
["elf"] = new byte[]{0x7F,(byte)'E',(byte)'L',(byte)'F'}, // ELF executable
["uboot"] = new byte[]{0x27,0x05,0x19,0x56}, // U-Boot image header
["squashfs"] = System.Text.Encoding.ASCII.GetBytes("hsqs"), // SquashFS superblock
["jffs2"] = new byte[]{0xEF,0x53}, // JFFS2 magic
["lzma"] = new byte[]{0x5D,0x00,0x00}, // LZMA header
["tar"] = System.Text.Encoding.ASCII.GetBytes("ustar"), // POSIX tar
["zip"] = new byte[]{0x50,0x4B,0x03,0x04}, // ZIP archive
["jpeg"] = new byte[]{0xFF,0xD8,0xFF}, // JPEG image
["png"] = new byte[]{0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A}, // PNG image
["xz"] = new byte[]{0xFD,(byte)'7',(byte)'z',(byte)'X',(byte)'Z',0x00}, // XZ compressed
["lz4"] = new byte[]{0x04,0x22,0x4D,0x18}, // LZ4 frame
["bzip2"] = new byte[]{0x42,0x5A,0x68}, // BZIP2 stream
["rar"] = new byte[]{0x52,0x61,0x72,0x21,0x1A,0x07,0x00}, // RAR archive
["zstd"] = new byte[]{0x28,0xB5,0x2F,0xFD}, // Zstandard frame
["lzop"] = new byte[]{0x89,0x4C,0x5A,0x4F,0x00,0x0D,0x0A,0x1A,0x0A}, // LZOP header
// Additional common magics
["7z"] = new byte[]{0x37,0x7A,0xBC,0xAF,0x27,0x1C}, // 7-Zip archive header
["exe"] = new byte[]{0x4D,0x5A}, // Windows MZ executable
["pdf"] = System.Text.Encoding.ASCII.GetBytes("%PDF"), // PDF document
["bmp"] = new byte[]{0x42,0x4D}, // BMP image
["macho32"] = new byte[]{0xCE,0xFA,0xED,0xFE}, // Mach-O 32-bit
["macho64"] = new byte[]{0xCF,0xFA,0xED,0xFE}, // Mach-O 64-bit
["iso9660"] = System.Text.Encoding.ASCII.GetBytes("CD001"), // ISO9660 volume descriptor
["cab"] = System.Text.Encoding.ASCII.GetBytes("MSCF") // CAB archive header
};
var found = sigs.Select(kv=>(kv.Key, off:FindPattern(buf,kv.Value,0)))
.Where(x=>x.off>=0)
.OrderBy(x=>x.off)
.ToList();
// If there is data before the first signature, extract it as preamble
if (found.Count > 0 && found[0].off > 0)
{
int len0 = found[0].off;
Console.WriteLine($"[ArchiveExtractor] extracting 'preamble' at 0x0 ({len0} bytes)");
File.WriteAllBytes(Path.Combine(outDir, $"preamble_0x0.bin"), buf.Take(len0).ToArray());
}
for (int i = 0; i < found.Count; i++)
{
var (name, off) = found[i];
int end = (i + 1 < found.Count) ? found[i + 1].off : buf.Length;
int length = end - off;
Console.WriteLine($"[ArchiveExtractor] extracting '{name}' at 0x{off:X} ({length} bytes)");
var outputFile = Path.Combine(outDir, $"{name}_{off:X}.bin");
File.WriteAllBytes(outputFile, buf.Skip(off).Take(length).ToArray());
// If YAFFS filesystem detected, run YAFFS extractor stub
if (name.Equals("yaffs", StringComparison.OrdinalIgnoreCase))
{
var yaffsOut = Path.Combine(outDir, $"{name}_{off:X}_extracted");
YaffsExtractor.ExtractYaffs(outputFile, yaffsOut);
}
// If TRX container detected, run TRX extractor
if (name.Equals("trx", StringComparison.OrdinalIgnoreCase))
{
var trxOut = Path.Combine(outDir, $"{name}_{off:X}_extracted");
TrxExtractor.ExtractTrx(outputFile, trxOut);
}
// If Mediaroom XMI detected, run XMI extractor stub
if (name.Equals("xmi", StringComparison.OrdinalIgnoreCase))
{
var xmiOut = Path.Combine(outDir, $"{name}_{off:X}_extracted");
XmiExtractor.ExtractXmi(outputFile, xmiOut);
}
}
}
private static int FindPattern(byte[] data, byte[] patt, int start)
{
for(int i=start;i<=data.Length-patt.Length;i++)
{
bool ok=true;
for(int j=0;j<patt.Length;j++) if(data[i+j]!=patt[j]){ok=false;break;}
if(ok) return i;
}
return -1;
}
private static void Sanitize(string dir)
{
try
{
string root=Path.GetFullPath(dir).TrimEnd(Path.DirectorySeparatorChar)+"\\";
foreach(var f in Directory.GetFiles(dir,"*",SearchOption.AllDirectories))
if(!Path.GetFullPath(f).StartsWith(root,StringComparison.OrdinalIgnoreCase))
File.Delete(f);
}catch{}
}
private static string Resolve7zExecutable()
{
string[] cand={
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),"7-Zip","7z.exe"),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),"7-Zip","7z.exe")
};
foreach(var p in cand) if(File.Exists(p)) return p;
var env=Environment.GetEnvironmentVariable("PATH");
foreach(var d in (env??"").Split(';'))
try{var ex=Path.Combine(d.Trim(),"7z.exe");if(File.Exists(ex))return ex;}catch{}
var dlg=new OpenFileDialog{Title="Locate 7z.exe",Filter="7z.exe"};
return dlg.ShowDialog()==true?dlg.FileName:null;
}
}
}