6 Strip relocation section from Win32 PE files
\r
7 Copyright (C) 2000-2003 Jordan Russell. All rights reserved.
\r
9 www: http://www.jrsoftware.org/
\r
10 email: jr AT jrsoftware.org
\r
12 This program is free software; you can redistribute it and/or
\r
13 modify it under the terms of the GNU General Public License
\r
14 as published by the Free Software Foundation; either version 2
\r
15 of the License, or (at your option) any later version.
\r
17 This program is distributed in the hope that it will be useful,
\r
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
20 GNU General Public License for more details.
\r
22 You should have received a copy of the GNU General Public License
\r
23 along with this program; if not, write to the Free Software
\r
24 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
28 Windows, SysUtils, Classes;
\r
36 KeepBackups: Boolean = True;
\r
37 WantValidChecksum: Boolean = False;
\r
38 ForceStrip: Boolean = False;
\r
40 ImageHlpHandle: THandle;
\r
41 CheckSumMappedFile: function(BaseAddress: Pointer; FileLength: DWORD;
\r
42 HeaderSum: PDWORD; CheckSum: PDWORD): PImageNtHeaders; stdcall;
\r
44 function CalcChecksum(const FileHandle: THandle): DWORD;
\r
51 H := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
\r
53 RaiseLastWin32Error;
\r
55 M := MapViewOfFile(H, FILE_MAP_READ, 0, 0, 0);
\r
56 Win32Check(CheckSumMappedFile(M, GetFileSize(FileHandle, nil), @OldSum, @Result) <> nil);
\r
64 procedure Strip(const Filename: String);
\r
66 PPESectionHeaderArray = ^TPESectionHeaderArray;
\r
67 TPESectionHeaderArray = array[0..$7FFFFFFF div SizeOf(TImageSectionHeader)-1] of TImageSectionHeader;
\r
69 BackupFilename: String;
\r
72 PEHeaderOffset, PESig: Cardinal;
\r
73 PEHeader: TImageFileHeader;
\r
74 PEOptHeader: ^TImageOptionalHeader;
\r
75 PESectionHeaders: PPESectionHeaderArray;
\r
76 BytesLeft, Bytes: Cardinal;
\r
77 Buf: array[0..8191] of Byte;
\r
79 RelocVirtualAddr, RelocPhysOffset, RelocPhysSize: Cardinal;
\r
80 OldSize, NewSize: Cardinal;
\r
81 TimeStamp: TFileTime;
\r
84 PESectionHeaders := nil;
\r
86 RelocPhysOffset := 0;
\r
88 BackupFilename := Filename + '.bak';
\r
90 Write(Filename, ': ');
\r
91 AssignFile(F, Filename);
\r
92 FileMode := fmOpenRead or fmShareDenyWrite;
\r
95 OldSize := FileSize(F);
\r
96 GetFileTime(TFileRec(F).Handle, nil, nil, @TimeStamp);
\r
98 BlockRead(F, EXESig, SizeOf(EXESig));
\r
99 if EXESig <> $5A4D {'MZ'} then begin
\r
100 Writeln('File isn''t an EXE file (1).');
\r
104 BlockRead(F, PEHeaderOffset, SizeOf(PEHeaderOffset));
\r
105 if PEHeaderOffset = 0 then begin
\r
106 Writeln('File isn''t a PE file (1).');
\r
109 Seek(F, PEHeaderOffset);
\r
110 BlockRead(F, PESig, SizeOf(PESig));
\r
111 if PESig <> $00004550 {'PE'#0#0} then begin
\r
112 Writeln('File isn''t a PE file (2).');
\r
115 BlockRead(F, PEHeader, SizeOf(PEHeader));
\r
116 if not ForceStrip and (PEHeader.Characteristics and IMAGE_FILE_DLL <> 0) then begin
\r
117 Writeln('Skipping; can''t strip a DLL.');
\r
120 if PEHeader.Characteristics and IMAGE_FILE_RELOCS_STRIPPED <> 0 then begin
\r
121 Writeln('Relocations already stripped from file (1).');
\r
124 PEHeader.Characteristics := PEHeader.Characteristics or IMAGE_FILE_RELOCS_STRIPPED;
\r
125 GetMem(PEOptHeader, PEHeader.SizeOfOptionalHeader);
\r
126 BlockRead(F, PEOptHeader^, PEHeader.SizeOfOptionalHeader);
\r
127 if (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0) or
\r
128 (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0) then begin
\r
129 Writeln('Relocations already stripped from file (2).');
\r
132 RelocVirtualAddr := PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
\r
133 PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress := 0;
\r
134 PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size := 0;
\r
135 if not WantValidChecksum then
\r
136 PEOptHeader.CheckSum := 0;
\r
137 GetMem(PESectionHeaders, PEHeader.NumberOfSections * SizeOf(TImageSectionHeader));
\r
138 BlockRead(F, PESectionHeaders^, PEHeader.NumberOfSections * SizeOf(TImageSectionHeader));
\r
139 for I := 0 to PEHeader.NumberOfSections-1 do
\r
140 with PESectionHeaders[I] do
\r
141 if (VirtualAddress = RelocVirtualAddr) and (SizeOfRawData <> 0) then begin
\r
142 RelocPhysOffset := PointerToRawData;
\r
143 RelocPhysSize := SizeOfRawData;
\r
144 PointerToRawData := 0;
\r
145 SizeOfRawData := 0;
\r
148 if RelocPhysOffset = 0 then begin
\r
149 Writeln('Relocations already stripped from file (3).');
\r
152 for I := 0 to PEHeader.NumberOfSections-1 do
\r
153 with PESectionHeaders[I] do begin
\r
154 if PointerToRawData >= RelocPhysOffset then
\r
155 Dec(PointerToRawData, RelocPhysSize);
\r
156 if PointerToLinenumbers >= RelocPhysOffset then
\r
157 Dec(PointerToLinenumbers, RelocPhysSize);
\r
158 if PointerToRelocations <> 0 then begin
\r
159 { ^ I don't think this field is ever used in the PE format.
\r
160 StripRlc doesn't handle it. }
\r
161 Writeln('Cannot handle this file (1).');
\r
165 if PEOptHeader.ImageBase < $400000 then begin
\r
166 Writeln('Cannot handle this file -- the image base address is less than 0x400000.');
\r
172 if FileExists(BackupFilename) then
\r
173 Win32Check(DeleteFile(BackupFilename));
\r
174 Rename(F, BackupFilename);
\r
176 FileMode := fmOpenRead or fmShareDenyWrite;
\r
179 AssignFile(F2, Filename);
\r
180 FileMode := fmOpenWrite or fmShareExclusive;
\r
183 BytesLeft := RelocPhysOffset;
\r
184 while BytesLeft <> 0 do begin
\r
185 Bytes := BytesLeft;
\r
186 if Bytes > SizeOf(Buf) then Bytes := SizeOf(Buf);
\r
187 BlockRead(F, Buf, Bytes);
\r
188 BlockWrite(F2, Buf, Bytes);
\r
189 Dec(BytesLeft, Bytes);
\r
191 Seek(F, Cardinal(FilePos(F)) + RelocPhysSize);
\r
192 BytesLeft := FileSize(F) - FilePos(F);
\r
193 while BytesLeft <> 0 do begin
\r
194 Bytes := BytesLeft;
\r
195 if Bytes > SizeOf(Buf) then Bytes := SizeOf(Buf);
\r
196 BlockRead(F, Buf, Bytes);
\r
197 BlockWrite(F2, Buf, Bytes);
\r
198 Dec(BytesLeft, Bytes);
\r
200 Seek(F2, PEHeaderOffset + SizeOf(PESig));
\r
201 BlockWrite(F2, PEHeader, SizeOf(PEHeader));
\r
202 BlockWrite(F2, PEOptHeader^, PEHeader.SizeOfOptionalHeader);
\r
203 BlockWrite(F2, PESectionHeaders^, PEHeader.NumberOfSections * SizeOf(TImageSectionHeader));
\r
204 if WantValidChecksum then begin
\r
205 PEOptHeader.CheckSum := CalcChecksum(TFileRec(F2).Handle);
\r
206 { go back and rewrite opt. header with new checksum }
\r
207 Seek(F2, PEHeaderOffset + SizeOf(PESig) + SizeOf(PEHeader));
\r
208 BlockWrite(F2, PEOptHeader^, PEHeader.SizeOfOptionalHeader);
\r
210 NewSize := FileSize(F2);
\r
211 SetFileTime(TFileRec(F2).Handle, nil, nil, @TimeStamp);
\r
219 DeleteFile(Filename);
\r
220 AssignFile(F, BackupFilename);
\r
221 Rename(F, Filename);
\r
224 Writeln(OldSize, ' -> ', NewSize, ' bytes (',
\r
225 OldSize - NewSize, ' difference)');
\r
226 if not KeepBackups then
\r
227 if not DeleteFile(BackupFilename) then
\r
228 Writeln('Warning: Couldn''t delete backup file ', BackupFilename);
\r
230 FreeMem(PESectionHeaders);
\r
231 FreeMem(PEOptHeader);
\r
238 FilesList: TStringList;
\r
240 HasFileParameter: Boolean = False;
\r
241 NumFiles: Integer = 0;
\r
245 Writeln('StripReloc v' + Version + ', Copyright (C) 2000-2003 Jordan Russell, www.jrsoftware.org');
\r
246 if ParamCount = 0 then begin
\r
247 Writeln('Strip relocation section from Win32 PE files');
\r
249 1:Writeln('usage: stripreloc [switches] filename.exe');
\r
251 Writeln('switches: /B don''t create .bak backup files');
\r
252 Writeln(' /C write a valid checksum in the header (instead of zero)');
\r
253 Writeln(' /F force stripping DLLs instead of skipping them. do not use!');
\r
258 for P := 1 to ParamCount do begin
\r
260 if S[1] <> '/' then
\r
264 while I <= Length(S) do begin
\r
265 case UpCase(S[I]) of
\r
268 KeepBackups := False;
\r
269 if I < Length(S) then begin
\r
270 { For backward compatibility, do keep backups if the character
\r
271 following 'B' is a '+'. }
\r
272 if S[I+1] = '+' then begin
\r
273 KeepBackups := True;
\r
276 else if S[I+1] = '-' then
\r
281 ImageHlpHandle := LoadLibrary('imagehlp.dll');
\r
282 if ImageHlpHandle = 0 then begin
\r
283 Writeln('Error: Unable to load imagehlp.dll.');
\r
284 Writeln(' It is required when using the /C parameter.');
\r
287 CheckSumMappedFile := GetProcAddress(ImageHlpHandle, 'CheckSumMappedFile');
\r
288 if @CheckSumMappedFile = nil then begin
\r
289 Writeln('Error: Unable to get address of CheckSumMappedFile in imagehlp.dll.');
\r
290 Writeln(' It is required when using the /C parameter.');
\r
293 WantValidChecksum := True;
\r
295 'F': ForceStrip := True;
\r
297 Writeln('Invalid parameter: /', S[I]);
\r
304 for P := 1 to ParamCount do begin
\r
308 HasFileParameter := True;
\r
309 FilesList := TStringList.Create;
\r
311 FilesList.Sorted := True;
\r
312 if FindFirst(S, 0, SR) <> 0 then begin
\r
313 Writeln('No files matching "', S, '" found.');
\r
318 if CompareText(ExtractFileExt(SR.Name), '.bak') <> 0 then
\r
319 FilesList.Add(ExtractFilePath(S) + SR.Name);
\r
320 until FindNext(SR) <> 0;
\r
324 for I := 0 to FilesList.Count-1 do
\r
325 Strip(FilesList[I]);
\r
331 if not HasFileParameter then
\r
333 if NumFiles = 0 then
\r
336 on E: Exception do begin
\r
337 Writeln('Fatal error: ', E.Message);
\r