-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnalyzeHeader.cs
More file actions
235 lines (201 loc) · 7.93 KB
/
Copy pathAnalyzeHeader.cs
File metadata and controls
235 lines (201 loc) · 7.93 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
using SteamStubRemover.Core.PE32;
using SteamStubRemover.Core.PE64;
if (args.Length < 1)
{
Console.WriteLine("Usage: AnalyzeHeader <protected.exe> [steamless-unpacked.exe]");
return 1;
}
var protectedFile = args[0];
var steamlessFile = args.Length > 1 ? args[1] : null;
if (!File.Exists(protectedFile))
{
Console.WriteLine($"Error: File not found: {protectedFile}");
return 1;
}
Console.WriteLine("=== DEEP UNKNOWN FIELD ANALYSIS ===");
Console.WriteLine($"Protected file: {Path.GetFileName(protectedFile)}\n");
// Parse PE
var fileData = File.ReadAllBytes(protectedFile);
var peOffset = BitConverter.ToInt32(fileData, 0x3C);
var machine = BitConverter.ToUInt16(fileData, peOffset + 4);
var is64Bit = machine == 0x8664;
var pe64 = is64Bit ? new PeFile64(protectedFile) : null;
var pe32 = !is64Bit ? new PeFile32(protectedFile) : null;
pe64?.Parse();
pe32?.Parse();
var imageBase = is64Bit ? pe64!.NtHeaders.OptionalHeader.ImageBase : pe32!.NtHeaders.OptionalHeader.ImageBase;
var currentEp = is64Bit
? pe64!.NtHeaders.OptionalHeader.AddressOfEntryPoint
: pe32!.NtHeaders.OptionalHeader.AddressOfEntryPoint;
// Get bind section info - handle the different struct types separately
uint bindVa, bindVirtualSize, bindRawSize, bindRawPtr;
byte[] bindData;
if (is64Bit)
{
var section = pe64!.GetSection(".bind");
bindVa = section.VirtualAddress;
bindVirtualSize = section.VirtualSize;
bindRawSize = section.SizeOfRawData;
bindRawPtr = section.PointerToRawData;
bindData = pe64.GetSectionData(".bind")!;
}
else
{
var section = pe32!.GetSection(".bind");
bindVa = section.VirtualAddress;
bindVirtualSize = section.VirtualSize;
bindRawSize = section.SizeOfRawData;
bindRawPtr = section.PointerToRawData;
bindData = pe32.GetSectionData(".bind")!;
}
// Find and decrypt header
var headerOffset = FindHeader(bindData, imageBase);
if (headerOffset < 0)
{
Console.WriteLine("ERROR: Could not find DRM header!");
return 1;
}
var h = new byte[256];
Array.Copy(bindData, headerOffset, h, 0, Math.Min(256, bindData.Length - headerOffset));
var xorKey = BitConverter.ToUInt32(h, 0);
for (var i = 4; i < h.Length; i += 4)
{
var v = BitConverter.ToUInt32(h, i);
var d = v ^ xorKey;
BitConverter.TryWriteBytes(h.AsSpan(i), d);
xorKey = v;
}
var signature = BitConverter.ToUInt32(h, 0x04);
Console.WriteLine($"Architecture: {(is64Bit ? "x64" : "x86")}");
Console.WriteLine($"Signature: 0x{signature:X8}");
Console.WriteLine($"Header at .bind offset: 0x{headerOffset:X}\n");
// Get PE info for cross-referencing
Console.WriteLine("=== PE STRUCTURE REFERENCE VALUES ===");
Console.WriteLine($"ImageBase: 0x{imageBase:X}");
Console.WriteLine($"Current EP (stub): 0x{currentEp:X8}");
Console.WriteLine($".bind VA: 0x{bindVa:X8}");
Console.WriteLine($".bind VirtualSize: 0x{bindVirtualSize:X8}");
Console.WriteLine($".bind RawSize: 0x{bindRawSize:X8}");
Console.WriteLine($".bind RawPtr: 0x{bindRawPtr:X8}");
// Calculate some expected values
var epOffsetInBind = currentEp - bindVa;
Console.WriteLine($"EP offset in .bind: 0x{epOffsetInBind:X8}");
// Now analyze each unknown field
Console.WriteLine("\n=== UNKNOWN FIELD ANALYSIS ===\n");
// Unknown0000 at 0x1C
var unknown0000 = BitConverter.ToUInt32(h, 0x1C);
Console.WriteLine($"BindCodeSize (0x1C) = 0x{unknown0000:X8} ({unknown0000})");
Console.WriteLine($" - Header offset in .bind: 0x{headerOffset:X}");
Console.WriteLine($" - EP offset in .bind: 0x{epOffsetInBind:X}");
Console.WriteLine($" - Difference (header - EP): 0x{headerOffset - epOffsetInBind:X}");
if (unknown0000 == headerOffset - epOffsetInBind || unknown0000 == epOffsetInBind)
Console.WriteLine(" >>> LIKELY: Offset from EP to header OR code stub size");
else if (unknown0000 < 0x10000)
Console.WriteLine(" >>> LIKELY: Small offset or size value");
Console.WriteLine();
// Unknown0001 at 0x28
var unknown0001 = BitConverter.ToUInt32(h, 0x28);
Console.WriteLine($"StringTableOffset (0x28) = 0x{unknown0001:X8} ({unknown0001})");
if (unknown0001 == 0)
Console.WriteLine(" >>> Appears unused (zero)");
else
Console.WriteLine(" >>> Non-zero - likely an offset or count");
Console.WriteLine();
// Unknown0002 at 0x44
var unknown0002 = BitConverter.ToUInt32(h, 0x44);
Console.WriteLine($"HeaderHash (0x44) = 0x{unknown0002:X8}");
// Check if it looks like a hash (high bits set, not a small number)
if ((unknown0002 & 0xFF000000) != 0 && unknown0002 > 0x10000000)
Console.WriteLine(" >>> LIKELY: Hash/checksum value (high bits set, large value)");
else if (unknown0002 < 0x10000)
Console.WriteLine(" >>> LIKELY: Small offset or size");
else
Console.WriteLine(" >>> Unknown purpose");
Console.WriteLine();
// Unknown0003 at 0xA8 (32 bytes / 8 uints)
Console.WriteLine("Reserved (0xA8-0xC8) - 32 bytes:");
var allZero = true;
for (var i = 0; i < 8; i++)
{
var val = BitConverter.ToUInt32(h, 0xA8 + i * 4);
if (val != 0) allZero = false;
}
if (allZero)
Console.WriteLine(" >>> All zeros - likely reserved/padding");
else
Console.WriteLine(" >>> Has data - purpose unknown");
Console.WriteLine();
// Cross-reference with BindSectionOffset
var bindSectionOffset = BitConverter.ToUInt32(h, 0x18);
Console.WriteLine($"BindSectionOffset (0x18) = 0x{bindSectionOffset:X8} ({bindSectionOffset})");
Console.WriteLine(" - This should be: RVA(AddressOfEntryPoint - BindSectionOffset) = start of .bind data");
var stubEp = BitConverter.ToUInt64(h, 0x10);
Console.WriteLine($" - Stub EP: 0x{stubEp:X}");
Console.WriteLine($" - Stub EP - BindSectionOffset: 0x{stubEp - bindSectionOffset:X}");
Console.WriteLine($" - .bind VA: 0x{bindVa:X}");
if ((uint)(stubEp - bindSectionOffset) == bindVa)
Console.WriteLine(" >>> CONFIRMED: BindSectionOffset is correct");
Console.WriteLine();
// Analyze the EncryptionKeys
Console.WriteLine("EncryptionKeys (0x98) - XTEA keys for DRM DLL:");
for (var i = 0; i < 4; i++)
{
var key = BitConverter.ToUInt32(h, 0x98 + i * 4);
Console.WriteLine($" Key[{i}] = 0x{key:X8}");
}
Console.WriteLine();
// Validate against steamless if available
if (steamlessFile != null && File.Exists(steamlessFile))
{
Console.WriteLine("=== STEAMLESS VALIDATION ===");
uint steamlessEp;
if (is64Bit)
{
var s = new PeFile64(steamlessFile);
s.Parse();
steamlessEp = s.NtHeaders.OptionalHeader.AddressOfEntryPoint;
}
else
{
var s = new PeFile32(steamlessFile);
s.Parse();
steamlessEp = s.NtHeaders.OptionalHeader.AddressOfEntryPoint;
}
var oep = BitConverter.ToUInt64(h, 0x20);
Console.WriteLine($"Our OEP (0x20): 0x{oep:X}");
Console.WriteLine($"Steamless EP: 0x{steamlessEp:X}");
Console.WriteLine($"Match: {(uint)oep == steamlessEp}");
}
// Summary
Console.WriteLine("\n=== FIELD IDENTIFICATION SUMMARY ===");
Console.WriteLine("Based on Steamless comments (Cyanic) and analysis:");
Console.WriteLine(" BindCodeSize (0x1C): Size of unpacker stub code before header");
Console.WriteLine($" StringTableOffset (0x28): {(unknown0001 == 0 ? "Unused/Reserved" : "Offset to string table")}");
Console.WriteLine(" HeaderHash (0x44): Hash/checksum value for validation");
Console.WriteLine(" Reserved (0xA8): Unused padding");
return 0;
static int FindHeader(byte[] bind, ulong expectedImageBase)
{
for (var start = 0; start < bind.Length - 256; start += 4)
{
var key = BitConverter.ToUInt32(bind, start);
if (key == 0) continue;
var test = new byte[16];
Array.Copy(bind, start, test, 0, 16);
var k = key;
for (var i = 4; i < 16; i += 4)
{
var v = BitConverter.ToUInt32(test, i);
var d = v ^ k;
BitConverter.TryWriteBytes(test.AsSpan(i), d);
k = v;
}
var sig = BitConverter.ToUInt32(test, 4);
if (sig != 0xC0DEC0DE && sig != 0xC0DEC0DF)
continue;
var imgBase = BitConverter.ToUInt64(test, 8);
if (imgBase == expectedImageBase)
return start;
}
return -1;
}