-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbootloader.cs
More file actions
148 lines (129 loc) · 4.72 KB
/
bootloader.cs
File metadata and controls
148 lines (129 loc) · 4.72 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
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Security.Cryptography;
internal static class Program
{
private const string MarkerText = "PREFIXSFX1";
private static readonly byte[] MarkerBytes = Encoding.ASCII.GetBytes(MarkerText);
// marker + payload length (int64) + SHA256 (32 bytes)
private static readonly int FooterLength = MarkerText.Length + 8 + 32;
private const string ManifestName = "__main_path.txt";
public static int Main(string[] args)
{
string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string tempRoot = Path.Combine(Path.GetTempPath(), "pre_sfx_" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempRoot);
string zipPath = Path.Combine(tempRoot, "payload.zip");
try
{
ExtractPayload(exePath, zipPath);
ZipFile.ExtractToDirectory(zipPath, tempRoot);
File.Delete(zipPath);
string manifestPath = Path.Combine(tempRoot, ManifestName);
if (!File.Exists(manifestPath))
throw new InvalidOperationException("Manifest not found: " + manifestPath);
string mainRel = File.ReadAllText(manifestPath).Trim();
if (string.IsNullOrWhiteSpace(mainRel))
throw new InvalidOperationException("Main script path missing from manifest.");
string prePath = Path.Combine(tempRoot, "prefix.exe");
if (!File.Exists(prePath))
throw new InvalidOperationException("Bundled prefix.exe is missing.");
string mainPath = Path.Combine(tempRoot, mainRel);
var psi = new ProcessStartInfo
{
FileName = prePath,
Arguments = Quote(mainPath),
WorkingDirectory = tempRoot,
UseShellExecute = false,
};
var proc = Process.Start(psi);
if (proc == null)
throw new InvalidOperationException("Failed to start prefix.exe");
proc.WaitForExit();
return proc.ExitCode;
}
catch (Exception ex)
{
Console.Error.WriteLine("Prefix SFX error: " + ex);
return 1;
}
finally
{
try { Directory.Delete(tempRoot, true); } catch { /* best-effort */ }
}
}
private static string Quote(string path)
{
return "\"" + path + "\"";
}
private static void ExtractPayload(string exePath, string zipPath)
{
using (FileStream fs = new FileStream(exePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
if (fs.Length < FooterLength)
throw new InvalidOperationException("Executable is missing payload footer.");
fs.Seek(-MarkerBytes.Length, SeekOrigin.End);
var markerBuffer = new byte[MarkerBytes.Length];
int markerRead = fs.Read(markerBuffer, 0, markerBuffer.Length);
if (markerRead != markerBuffer.Length)
throw new InvalidOperationException("Failed to read marker.");
if (!EqualBytes(markerBuffer, MarkerBytes))
throw new InvalidOperationException("Marker not found; file is not a valid Prefix SFX.");
fs.Seek(-FooterLength, SeekOrigin.End);
long payloadLen = ReadInt64(fs);
var storedSha = new byte[32];
int shaRead = fs.Read(storedSha, 0, storedSha.Length);
if (shaRead != storedSha.Length)
throw new InvalidOperationException("Failed to read stored payload SHA256.");
if (payloadLen <= 0 || payloadLen > fs.Length)
throw new InvalidOperationException("Invalid payload length in footer.");
long payloadOffset = fs.Length - FooterLength - payloadLen;
if (payloadOffset < 0)
throw new InvalidOperationException("Calculated payload offset is invalid.");
fs.Seek(payloadOffset, SeekOrigin.Begin);
using (FileStream outFs = new FileStream(zipPath, FileMode.Create, FileAccess.Write, FileShare.None))
using (var sha = SHA256.Create())
{
byte[] buffer = new byte[8192];
long remaining = payloadLen;
while (remaining > 0)
{
int toRead = (int)Math.Min(buffer.Length, remaining);
int read = fs.Read(buffer, 0, toRead);
if (read == 0)
throw new EndOfStreamException("Unexpected end of file while copying payload.");
sha.TransformBlock(buffer, 0, read, null, 0);
outFs.Write(buffer, 0, read);
remaining -= read;
}
sha.TransformFinalBlock(new byte[0], 0, 0);
byte[] computed = sha.Hash;
if (!EqualBytes(computed, storedSha))
{
try { outFs.Close(); File.Delete(zipPath); } catch { }
throw new InvalidOperationException("Payload SHA256 does not match; file may be corrupt.");
}
}
}
}
private static long ReadInt64(Stream stream)
{
byte[] buffer = new byte[8];
int read = stream.Read(buffer, 0, buffer.Length);
if (read != buffer.Length)
throw new InvalidOperationException("Failed to read payload length.");
return BitConverter.ToInt64(buffer, 0);
}
private static bool EqualBytes(byte[] a, byte[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i]) return false;
}
return true;
}
}