forked from vdisasm/pe-image-for-delphi
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathPE.DataDirectories.pas
356 lines (297 loc) · 9.29 KB
/
PE.DataDirectories.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
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
unit PE.DataDirectories;
interface
uses
Classes,
SysUtils,
PE.Common,
PE.Msg,
PE.Section,
PE.Sections,
PE.Types.Directories,
PE.Utils;
type
TDataDirectories = class
private
FPE: TObject; // TPEImage
FItems: array of TImageDataDirectory;
function GetCount: integer; inline;
procedure SetCount(const Value: integer);
public
constructor Create(APE: TObject);
procedure Clear;
procedure NullInvalid(const Msg: TMsgMgr);
// Load array of TImageDataDirectory (va,size) from stream.
procedure LoadDirectoriesFromStream(
Stream: TStream;
const Msg: TMsgMgr;
DeclaredCount: uint32; // # of rva and sizes
MaxBytes: uint16
);
// Save array of TImageDataDirectory (va,size) to stream.
// Return saved size.
function SaveDirectoriesToStream(Stream: TStream): integer;
// Check if index is in item range.
function IsGoodIndex(Index: integer): boolean; inline;
// Get TImageDataDirectory by Index.
// Result is True if Index exists.
// OutDir is optional and can be nil.
function Get(Index: integer; OutDir: PImageDataDirectory): boolean;
// Get directory name.
function GetName(Index: integer): string;
// Put directory safely. If Index > than current item count, empty items
// will be added.
procedure Put(Index: integer; const Dir: TImageDataDirectory); overload;
procedure Put(Index: integer; RVA, Size: uint32); overload;
// If Index exists set RVA and Size to 0.
procedure Null(Index: integer);
// Check if directory by Index occuppies whole section. If it's true,
// result is section. Otherwise result is nil.
function GetSectionDedicatedToDir(Index: integer;
AlignSize: boolean = False): TPESection;
// Get index of directory, which occupies whole Section. Result is -1 if
// not found.
function GetDirDedicatedToSection(Section: TPESection; AlignSize: boolean = False): integer;
// Save data pointed by directory RVA and Size to file.
// Index is directory index.
function SaveToStream(Index: integer; Stream: TStream): boolean;
function SaveToFile(Index: integer; const FileName: string): boolean;
// Load data into memory pointed by directory RVA and Size.
// Index is directory index.
// Offset is file offset where to start reading from.
// Result is True if complete size was read.
function LoadFromStream(Index: integer; Stream: TStream; Offset: uint64): boolean;
function LoadFromFile(Index: integer; const FileName: string; Offset: uint64): boolean;
property Count: integer read GetCount write SetCount;
end;
implementation
uses
// Expand
PE.Headers,
PE.Image;
{ TDataDirectories }
procedure TDataDirectories.Clear;
begin
FItems := nil;
self.Count := 0;
end;
constructor TDataDirectories.Create(APE: TObject);
begin
self.FPE := APE;
end;
function TDataDirectories.GetSectionDedicatedToDir(Index: integer;
AlignSize: boolean): TPESection;
var
Dir: TImageDataDirectory;
sec: TPESection;
ExpectedSize, VSize: uint32;
img: TPEImage;
begin
Result := nil;
if not Get(Index, @Dir) then
exit;
if Dir.IsEmpty then
exit;
img := TPEImage(FPE);
if not img.Sections.RVAToSec(Dir.VirtualAddress, @sec) then
exit;
if (sec.RVA <> Dir.VirtualAddress) then
exit;
if AlignSize then
begin
ExpectedSize := AlignUp(Dir.Size, img.SectionAlignment);
VSize := AlignUp(sec.VirtualSize, img.SectionAlignment);
end
else
begin
ExpectedSize := Dir.Size;
VSize := sec.VirtualSize;
end;
if (VSize = ExpectedSize) then
Result := sec;
end;
function TDataDirectories.IsGoodIndex(Index: integer): boolean;
begin
Result := (Index >= 0) and (Index < Length(FItems));
end;
function TDataDirectories.Get(Index: integer; OutDir: PImageDataDirectory): boolean;
begin
Result := IsGoodIndex(Index);
if Result and Assigned(OutDir) then
OutDir^ := FItems[Index];
end;
procedure TDataDirectories.Put(Index: integer; const Dir: TImageDataDirectory);
begin
if Index >= Length(FItems) then
SetLength(FItems, Index + 1)
else if Index < 0 then
Index := 0;
FItems[Index] := Dir;
end;
procedure TDataDirectories.Put(Index: integer; RVA, Size: uint32);
var
d: TImageDataDirectory;
begin
d.RVA := RVA;
d.Size := Size;
Put(Index, d);
end;
procedure TDataDirectories.Null(Index: integer);
begin
if (Index >= 0) and (Index < Length(FItems)) then
FItems[Index] := NULL_IMAGE_DATA_DIRECTORY;
end;
function TDataDirectories.GetCount: integer;
begin
Result := Length(FItems);
end;
function TDataDirectories.GetDirDedicatedToSection(Section: TPESection; AlignSize: boolean = False): integer;
var
i: integer;
begin
for i := 0 to Length(FItems) - 1 do
begin
if GetSectionDedicatedToDir(i, AlignSize) = Section then
exit(i);
end;
exit(-1);
end;
function TDataDirectories.GetName(Index: integer): string;
begin
if IsGoodIndex(Index) then
Result := GetDirectoryName(index)
else
Result := '';
end;
procedure TDataDirectories.NullInvalid(const Msg: TMsgMgr);
var
i: integer;
needToNullDir: boolean;
begin
// Check RVAs.
for i := 0 to self.Count - 1 do
begin
// Empty dir is ok.
if FItems[i].IsEmpty then
continue;
needToNullDir := False;
if (FItems[i].Size = 0) and (FItems[i].RVA <> 0) then
begin
Msg.Write(SCategoryDataDirecory, 'Directory # %d has size = 0.', [i]);
needToNullDir := true;
end
else if not TPEImage(FPE).RVAExists(FItems[i].RVA) then
begin
Msg.Write(SCategoryDataDirecory, 'Directory # %d RVA is not in image.', [i]);
needToNullDir := true;
end;
if needToNullDir and (PO_NULL_INVALID_DIRECTORY in TPEImage(FPE).Options) then
begin
FItems[i] := NULL_IMAGE_DATA_DIRECTORY;
Msg.Write(SCategoryDataDirecory, 'Directory # %d nulled.', [i]);
end;
end;
end;
procedure TDataDirectories.LoadDirectoriesFromStream;
var
MaxCountPossible: integer;
CountToRead: integer;
SizeToFileEnd: uint64;
Size: uint32;
MaxSizePossible: uint16;
begin
Clear;
if DeclaredCount = 0 then
begin
Msg.Write(SCategoryDataDirecory, 'No data directories.');
exit;
end;
SizeToFileEnd := (Stream.Size - Stream.Position);
// Max size available for dirs.
if SizeToFileEnd > MaxBytes then
MaxSizePossible := MaxBytes
else
MaxSizePossible := SizeToFileEnd;
MaxCountPossible := MaxSizePossible div SizeOf(TImageDataDirectory);
// File can have part of dword stored. It must be extended with zeros.
if (MaxSizePossible mod SizeOf(TImageDataDirectory)) <> 0 then
inc(MaxCountPossible);
if DeclaredCount <> TYPICAL_NUMBER_OF_DIRECTORIES then
Msg.Write(SCategoryDataDirecory, 'Non-usual count of directories declared (0x%x).', [DeclaredCount]);
if DeclaredCount > MaxCountPossible then
begin
CountToRead := MaxCountPossible;
Msg.Write(SCategoryDataDirecory,
'Declared count of directories is greater than file can contain (0x%x > 0x%x).',
[DeclaredCount, MaxCountPossible]);
Msg.Write(SCategoryDataDirecory, 'Fall back to 0x%x.', [MaxCountPossible]);
end
else
begin
CountToRead := DeclaredCount;
end;
// Read data directories.
Size := CountToRead * SizeOf(TImageDataDirectory);
SetLength(FItems, CountToRead);
// Must clear buffer, cause it can have partial values (filled with zeros).
FillChar(FItems[0], Size, 0);
// Not all readed size/rva can be valid. You must check rvas before use.
Stream.Read(FItems[0], Size);
// Set final count.
self.Count := CountToRead;
NullInvalid(Msg);
end;
function TDataDirectories.SaveToStream(Index: integer; Stream: TStream): boolean;
var
Dir: TImageDataDirectory;
begin
if Get(Index, @Dir) then
begin
TPEImage(FPE).SaveRegionToStream(Stream, Dir.VirtualAddress, Dir.Size);
exit(true);
end;
exit(False);
end;
function TDataDirectories.SaveToFile(Index: integer; const FileName: string): boolean;
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmCreate);
try
Result := SaveToStream(Index, fs);
finally
fs.Free;
end;
end;
function TDataDirectories.LoadFromStream(Index: integer; Stream: TStream; Offset: uint64): boolean;
var
Dir: TImageDataDirectory;
ReadCount: uint32;
begin
if Get(Index, @Dir) then
begin
TPEImage(FPE).LoadRegionFromStream(Stream, Offset, Dir.VirtualAddress, Dir.Size, @ReadCount);
exit(ReadCount = Dir.Size);
end;
exit(False);
end;
function TDataDirectories.LoadFromFile(Index: integer; const FileName: string; Offset: uint64): boolean;
var
fs: TFileStream;
begin
fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Result := LoadFromStream(Index, fs, Offset);
finally
fs.Free;
end;
end;
function TDataDirectories.SaveDirectoriesToStream(Stream: TStream): integer;
begin
Result := Stream.Write(FItems[0], Length(FItems) * SizeOf(TImageDataDirectory))
end;
procedure TDataDirectories.SetCount(const Value: integer);
begin
SetLength(FItems, Value);
TPEImage(FPE).OptionalHeader.NumberOfRvaAndSizes := Value;
end;
end.