-
Notifications
You must be signed in to change notification settings - Fork 80
/
PEInfo.pas
246 lines (207 loc) · 7.32 KB
/
PEInfo.pas
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
unit PEInfo;
interface
uses Windows, Classes, SysUtils;
type
TPESection = record
Header: TImageSectionHeader;
Data: PByte;
end;
PPESection = ^TPESection;
TPESections = TArray<TPESection>;
TPEHeader = class
private
FSections: TPESections;
FDumpSize: Cardinal;
FLFANew: Cardinal;
procedure FileAlign(var V: Cardinal);
procedure SectionAlign(var V: Cardinal);
public
NTHeaders: TImageNTHeaders;
constructor Create(Data: PByte);
function CreateSection(const Name: AnsiString; Size: Cardinal): PPESection;
procedure DeleteSection(Idx: Integer);
function GetSectionByVA(V: Cardinal): PPESection;
procedure AddSectionToArray;
function ConvertOffsetToRVAVector(Offset: NativeUInt): NativeUInt;
function TrimHugeSections(Buf: PByte; var IATRawAddr: Cardinal): Cardinal;
procedure Sanitize;
procedure SaveToStream(S: TStream);
property Sections: TPESections read FSections;
property LFANew: Cardinal read FLFANew;
property DumpSize: Cardinal read FDumpSize;
property SizeOfImage: Cardinal read NTHeaders.OptionalHeader.SizeOfImage;
end;
implementation
uses Utils;
{ TPEHeader }
constructor TPEHeader.Create(Data: PByte);
var
NT: PImageNTHeaders;
Sect: PImageSectionHeader;
i: Integer;
begin
FLFANew := PImageDosHeader(Data)._lfanew;
NT := PImageNTHeaders(Data + FLFANew);
NTHeaders := NT^;
FDumpSize := SizeOfImage;
Sect := PImageSectionHeader(PByte(NT) + SizeOf(TImageNTHeaders));
SetLength(FSections, NT.FileHeader.NumberOfSections);
for i := 0 to High(FSections) do
begin
FSections[i].Header := Sect^;
Inc(Sect);
end;
end;
function TPEHeader.CreateSection(const Name: AnsiString; Size: Cardinal): PPESection;
var
Prev: PImageSectionHeader;
begin
Prev := @FSections[High(FSections)].Header;
SetLength(FSections, Length(FSections) + 1);
Result := @FSections[High(FSections)];
FillChar(Result^, SizeOf(Result^), 0);
Move(Name[1], Result.Header.Name[0], Length(Name));
with Result.Header do
begin
Misc.VirtualSize := Size;
VirtualAddress := Prev.VirtualAddress + Prev.Misc.VirtualSize;
SectionAlign(VirtualAddress);
PointerToRawData := NTHeaders.OptionalHeader.SizeOfImage;
SizeOfRawData := Size;
Characteristics := IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;
end;
Inc(NTHeaders.OptionalHeader.SizeOfImage, Size);
// NumberOfSections is handled by the dumper
end;
procedure TPEHeader.DeleteSection(Idx: Integer);
var
i: Integer;
Sz: Cardinal;
IsLast: Boolean;
begin
IsLast := Idx = High(FSections);
if IsLast then
Sz := NTHeaders.OptionalHeader.SizeOfImage - FSections[Idx].Header.SizeOfRawData
else
Sz := FSections[Idx + 1].Header.PointerToRawData - FSections[Idx].Header.PointerToRawData;
for i := High(FSections) downto Idx + 1 do
begin
Dec(FSections[i].Header.PointerToRawData, Sz);
end;
Inc(FSections[Idx - 1].Header.Misc.VirtualSize, FSections[Idx].Header.Misc.VirtualSize);
SectionAlign(FSections[Idx - 1].Header.Misc.VirtualSize);
if not IsLast then
Move(FSections[Idx + 1], FSections[Idx], SizeOf(TPESection) * (High(FSections) - Idx));
SetLength(FSections, High(FSections));
Dec(NTHeaders.FileHeader.NumberOfSections);
end;
function TPEHeader.GetSectionByVA(V: Cardinal): PPESection;
var
i: Integer;
begin
for i := 0 to High(FSections) do
if FSections[i].Header.VirtualAddress + FSections[i].Header.Misc.VirtualSize > V then
Exit(@FSections[i]);
Result := nil;
end;
procedure TPEHeader.Sanitize;
var
i: Integer;
begin
for i := 0 to High(FSections) do
begin
with FSections[i].Header do
begin
PointerToRawData := VirtualAddress;
SizeOfRawData := Misc.VirtualSize;
end;
end;
NTHeaders.OptionalHeader.SizeOfHeaders := FSections[0].Header.PointerToRawData;
// Must have write access in code section (in case .text and .data were merged)
FSections[0].Header.Characteristics := FSections[0].Header.Characteristics or IMAGE_SCN_MEM_WRITE;
end;
procedure TPEHeader.SaveToStream(S: TStream);
var
i: Integer;
LulzMem: PByte;
begin
S.Seek(FLFANew, soBeginning);
S.Write(NTHeaders, SizeOf(NTHeaders));
for i := 0 to High(FSections) do
begin
S.Write(FSections[i].Header, SizeOf(TImageSectionHeader));
end;
// Zero out some leftovers that may be in the header
GetMem(LulzMem, $200);
FillChar(LulzMem^, $200, 0);
S.Write(LulzMem^, $200);
FreeMem(LulzMem);
end;
function TPEHeader.TrimHugeSections(Buf: PByte; var IATRawAddr: Cardinal): Cardinal;
var
i, j, ZeroStart: Integer;
SectionStart, OldSectionSize, NewSectionSize, Delta: Cardinal;
begin
Result := 0;
for i := 0 to NTHeaders.FileHeader.NumberOfSections - 1 do
begin
SectionStart := FSections[i].Header.PointerToRawData;
ZeroStart := -1;
for j := (FSections[i].Header.SizeOfRawData div 4) - 1 downto 0 do
if PCardinal(Buf + SectionStart + Cardinal(j) * 4)^ = 0 then
ZeroStart := j * 4
else
Break;
// We could reduce every single section to its minimal raw size, but having file offset = rva
// is pretty convenient, so we only trim sections that were obviously bloated up
if (ZeroStart <> -1) and (FSections[i].Header.SizeOfRawData - Cardinal(ZeroStart) > 1 * 1024 * 1024) then
begin
OldSectionSize := FSections[i].Header.SizeOfRawData;
SectionAlign(OldSectionSize); // Because of Sanitize(), the actual size is always section-aligned in our case
NewSectionSize := ZeroStart;
FileAlign(NewSectionSize);
Log(ltInfo, Format('Reducing size of section [%s]: %X -> %X', [Copy(AnsiString(PAnsiChar(@FSections[i].Header.Name)), 1, 8), OldSectionSize, NewSectionSize]));
Delta := OldSectionSize - NewSectionSize;
Inc(Result, Delta);
FSections[i].Header.SizeOfRawData := NewSectionSize;
if i < High(FSections) then
begin
Move(Buf[FSections[i + 1].Header.PointerToRawData], Buf[SectionStart + NewSectionSize], FDumpSize - SectionStart - OldSectionSize);
for j := i + 1 to High(FSections) do
Dec(FSections[j].Header.PointerToRawData, Delta);
end;
if IATRawAddr >= SectionStart + OldSectionSize then
Dec(IATRawAddr, Delta);
end;
end;
end;
procedure TPEHeader.AddSectionToArray;
begin
SetLength(FSections, Length(FSections) + 1);
end;
procedure TPEHeader.FileAlign(var V: Cardinal);
var
Delta: Cardinal;
begin
Delta := V mod NTHeaders.OptionalHeader.FileAlignment;
if Delta > 0 then
Inc(V, NTHeaders.OptionalHeader.FileAlignment - Delta);
end;
procedure TPEHeader.SectionAlign(var V: Cardinal);
var
Delta: Cardinal;
begin
Delta := V mod NTHeaders.OptionalHeader.SectionAlignment;
if Delta > 0 then
Inc(V, NTHeaders.OptionalHeader.SectionAlignment - Delta);
end;
function TPEHeader.ConvertOffsetToRVAVector(Offset: NativeUInt): NativeUInt;
var
i: Integer;
begin
for i := 0 to High(FSections) do
if (FSections[i].Header.PointerToRawData <= Offset) and ((FSections[i].Header.PointerToRawData + FSections[i].Header.SizeOfRawData) > Offset) then
Exit((Offset - FSections[i].Header.PointerToRawData) + FSections[i].Header.VirtualAddress);
Result := 0;
end;
end.