-
Notifications
You must be signed in to change notification settings - Fork 4
/
Patcher.pas
215 lines (187 loc) · 4.91 KB
/
Patcher.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
unit Patcher;
interface
uses
WinApi.Windows, System.SysUtils, System.Classes, System.Hash;
function Patch(fileName: string): boolean;
function DropDLLs: boolean;
function CheckOSVersion: boolean;
var
Log: TStringList;
implementation
function FileSize(const aFilename: String): Int64;
var
info: TWin32FileAttributeData;
begin
result := -1;
if not GetFileAttributesEx(PChar(aFilename), GetFileExInfoStandard, @info) then
exit;
Result := Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32);
end;
function PatchMem(pattern, buf: PAnsiChar; patternlen, buflen: integer): boolean;
var
i: integer;
begin
Result := False;
for i := 0 to buflen - patternlen - 1 do
begin
if CompareMem(pattern, @buf[i], patternlen) then
begin
Log.Add('patching at: 0x' + i.ToHexString(8));
buf[i] := 'a';
Result := True;
end;
end;
end;
function WriteFromResource(resName, fileName: string): boolean;
var
rs: TResourceStream;
fs: TFileStream;
begin
Result := False;
rs := TResourceStream.Create(HInstance, resName, RT_RCDATA);
try
if FileExists(fileName) then
begin
if THashMD5.GetHashString(rs) = THashMD5.GetHashStringFromFile(fileName) then
begin
Log.Add(fileName + ' is correct version, no change required.');
Result := True;
exit;
end;
// update the file
if not DeleteFile(fileName) then
begin
Log.Add('file ' + fileName + ' cannot be deleted, probably in use, trying to rename...');
if not RenameFile(fileName, fileName.Substring(0, fileName.Length - 1) + '_') then
begin
Log.Add('cannot rename it to ' + fileName.Substring(0, fileName.Length - 1) + '. check if it''s opened my another program.');
exit;
end;
end;
end;
rs.Seek(0, soFromBeginning);
try
{$WARNINGS OFF}
fs := TFileStream.Create(fileName, fmCreate or fmShareDenyRead);
{$WARNINGS ON}
except
Log.Add('cannot create file for writing: ' + fileName);
exit;
end;
fs.CopyFrom(rs);
fs.Free;
Log.Add('file written ok: ' + fileName);
Result := True;
finally
rs.Free;
end;
end;
function CheckOSVersion: boolean;
var
OSVersionInfoEx: TOSVersionInfoEx;
begin
Result := False;
Log.Clear;
OSVersionInfoEx.dwOSVersionInfoSize := sizeof(TOSVersionInfo);
if not GetVersionEx(OSVersionInfoEx) then
begin
Log.Add('cannot get OS version.');
exit;
end;
if (OSVersionInfoEx.dwMajorVersion <> 6) and
(OSVersionInfoEx.dwMinorVersion <> 1) then
begin
Log.Add('current OS is not Windows 7 or Server 2008.');
Log.Add('run this patcher on the actual Windows system you want to patch.');
exit;
end;
Result := True;
end;
function DropDLLs: boolean;
var
wd: string;
size1, size2: int64;
os64: boolean;
begin
Result := False;
Log.Clear;
Log.Add('checking additional files...');
SetLength(wd, MAX_PATH);
GetWindowsDirectory(PChar(wd), MAX_PATH);
wd := string(pchar(wd));
size1 := FileSize(wd + '\System32\kernel32.dll');
size2 := FileSize(wd + '\Sysnative\kernel32.dll');
if size1 = -1 then
begin
Log.Add('cannot get system DLL size.');
exit;
end;
os64 := size2 > size1;
if not WriteFromResource('dll32', wd + '\System32\acryptprimitives.dll') then
exit;
if os64 then
if not WriteFromResource('dll64', wd + '\Sysnative\acryptprimitives.dll') then
exit;
Log.Add('additional files are ok.');
Result := True;
end;
function Patch(fileName: string): boolean;
var
fs, bs: TFileStream;
ms: TMemoryStream;
buf: PAnsiChar;
c1, c2: PAnsiChar;
begin
Result := False;
Log.Clear;
Log.Add('checking file: ' + ExtractFileName(fileName) + '...');
try
{$WARNINGS OFF}
fs := TFileStream.Create(fileName, fmOpenReadWrite or fmShareDenyRead);
{$WARNINGS ON}
GetMem(buf, fs.Size);
fs.Read(buf^, fs.Size);
except
Log.Add('couldn''t read the file, check if it''s opened by another program.');
exit;
end;
ms := TMemoryStream.Create;
ms.CopyFrom(fs);
ms.Seek(0, soFromBeginning);
try
c1 := 'bcryptprimitives.dll';
c2 := @string(c1)[1];
if not(PatchMem(c1, buf, Length(c1), fs.Size) and PatchMem(c2, buf,
Length(c1) * 2, fs.Size)) then
begin
Log.Add('couldn''t find the required data to patch. file not changed.');
exit;
end;
// write the changes
fs.Seek(0, soFromBeginning);
fs.Write(buf^, fs.Size);
Log.Add('saved ok.');
// save file backup
try
{$WARNINGS OFF}
bs := TFileStream.Create(fileName + '.bak', fmCreate or fmShareDenyRead);
{$WARNINGS ON}
except
Log.Add('couldn''t save backup file ' + fileName + '.bak');
exit;
end;
bs.CopyFrom(ms);
bs.Free;
Log.Add('backup saved ok.');
finally
fs.Free;
ms.Free;
end;
Result := True;
Log.Add('file successfully patched.');
end;
initialization
Log := TStringList.Create;
finalization
Log.Free;
end.