-
Notifications
You must be signed in to change notification settings - Fork 0
/
gmkryptstream.pp
210 lines (174 loc) · 5.91 KB
/
gmkryptstream.pp
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
unit GmKryptStream;
{
gmkryptstream.pas
Custom TStream subclasses to deal with the GMKrypt encryption
used by GameMaker.
Written by Dmitry D. Chernov aka Black Doomer.
Based on the original GMKrypt description and sources of the
encoding and decoding algorithms by IsmAvatar and Quadduc.
}
{$MODE OBJFPC}
{$LONGSTRINGS ON}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
interface
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
uses
SysUtils, Classes;
const
// with this key, data essentially remains unencrypted in non-additive mode
cGmKryptIdentityKeySeed = 248;
////////////////////////////////////////////////////////////////////////////////////////////////////
type
TGmKryptSwapTable = array[Byte] of Byte;
TGmKryptStreamClass = class of TCustomGmKryptStream;
TCustomGmKryptStream = class( TOwnerStream )
strict protected
fEncodeTable : TGmKryptSwapTable;
fDecodeTable : TGmKryptSwapTable;
fBasePosition : Int64;
fKeySeed : LongInt;
fAdditiveCipher : Boolean;
protected
function GetPosition(): Int64; override;
function GetSize(): Int64; override;
public
procedure AfterConstruction(); override;
function SetState( aKeySeed: LongInt; aAdditiveCipher: Boolean ): Boolean; virtual;
function IsIdenticalState(): Boolean; inline;
function Seek( const Offset: Int64; Origin: TSeekOrigin ): Int64; override;
function Read( var Buffer; Count: LongInt ): LongInt; override;
function Write( const Buffer; Count: LongInt ): LongInt; override;
property KeySeed: LongInt read fKeySeed;
property AdditiveCipher: Boolean read fAdditiveCipher;
end;
TGmKryptStream = class( TCustomGmKryptStream )
// This class is provided to use it for ownership and context sharing. If you want to
// use crypto stream just as a regular data source, use TCustomGmKryptStream instead.
end;
function GmKryptRandomKeySeed(): LongInt;
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
function GmKryptRandomKeySeed(): LongInt;
begin
Result := Random( LongInt(25600) ) + 3328;
end;
////////////////////////////////////////////////////////////////////////////////////////////////////
// TCustomGmKryptStream
////////////////////////////////////////////////////////////////////////////////////////////////////
procedure TCustomGmKryptStream.AfterConstruction();
begin
SetState( cGmKryptIdentityKeySeed, False );
end;
function TCustomGmKryptStream.SetState( aKeySeed: LongInt; aAdditiveCipher: Boolean ): Boolean;
var
a, b : Integer;
swap : Byte;
i, j : Integer;
begin
fBasePosition := fSource.Position; // note: the first byte is always unencrypted
fKeySeed := aKeySeed;
fAdditiveCipher := aAdditiveCipher;
Result := IsIdenticalState();
if Result then exit;
for i in Byte do
fEncodeTable[i] := i;
a := (aKeySeed mod 250) + 6;
b := aKeySeed div 250;
for i := 1 to 10000 do begin
j := ( (i*a+b) mod 254 ) + 1;
swap := fEncodeTable[j];
fEncodeTable[j] := fEncodeTable[j+1];
fEncodeTable[j+1] := swap;
end;
for i in Byte do
fDecodeTable[ fEncodeTable[i] ] := i;
end;
function TCustomGmKryptStream.IsIdenticalState(): Boolean;
begin
Result := (fKeySeed - cGmKryptIdentityKeySeed) mod 250 = 0; // check for identity keys
Result := Result and not fAdditiveCipher; // check for non-identity method
end;
function TCustomGmKryptStream.Seek( const Offset: Int64; Origin: TSeekOrigin ): Int64;
var
BaseShift : Int64;
begin
if not IsIdenticalState() then begin
case Word(Origin) of
soFromBeginning:
BaseShift := 0;
soFromEnd:
BaseShift := GetSize() - 1;
else // soFromCurrent:
BaseShift := GetPosition();
end;
if Offset < -BaseShift then
InvalidSeek();
end;
Result := fSource.Seek( Offset, Origin );
end;
function TCustomGmKryptStream.Read( var Buffer; Count: LongInt ): LongInt;
var
TargetData : PByte;
code : Byte;
i : LongInt;
begin
Result := fSource.Read( Buffer, Count );
if IsIdenticalState() then exit;
// skip first byte as required
if GetPosition() = Result then
i := 1
else
i := 0;
TargetData := PByte(@Buffer);
for i := i to Result-1 do begin
code := fDecodeTable[ TargetData[i] ];
if fAdditiveCipher then
code := SizeInt(code - GetPosition() - i) and 255;
TargetData[i] := code;
end;
end;
function TCustomGmKryptStream.Write( const Buffer; Count: LongInt ): LongInt;
var
SourceData, OutputData : PByte;
code : Byte;
i : LongInt;
begin
if IsIdenticalState() then begin
SourceData := nil;
OutputData := @Buffer;
end else begin
SourceData := GetMemory( Count );
Move( Buffer, SourceData^, Count );
OutputData := SourceData;
// skip first byte as required
if GetPosition() = 0 then
i := 1
else
i := 0;
for i := i to Count-1 do begin
code := SourceData[i];
if fAdditiveCipher then
code := SizeInt(code + GetPosition() + i) and 255;
SourceData[i] := fEncodeTable[code];
end;
end;
try
Result := fSource.Write( OutputData^, Count );
finally
FreeMemory( SourceData );
end;
end;
function TCustomGmKryptStream.GetPosition(): Int64;
begin
Result := fSource.Position - fBasePosition;
end;
function TCustomGmKryptStream.GetSize(): Int64;
begin
Result := fSource.Size - fBasePosition;
end;
end.