linear-mcp-fast 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. ccl_chromium_reader/__init__.py +2 -0
  2. ccl_chromium_reader/ccl_chromium_cache.py +1335 -0
  3. ccl_chromium_reader/ccl_chromium_filesystem.py +302 -0
  4. ccl_chromium_reader/ccl_chromium_history.py +357 -0
  5. ccl_chromium_reader/ccl_chromium_indexeddb.py +1060 -0
  6. ccl_chromium_reader/ccl_chromium_localstorage.py +454 -0
  7. ccl_chromium_reader/ccl_chromium_notifications.py +268 -0
  8. ccl_chromium_reader/ccl_chromium_profile_folder.py +568 -0
  9. ccl_chromium_reader/ccl_chromium_sessionstorage.py +368 -0
  10. ccl_chromium_reader/ccl_chromium_snss2.py +332 -0
  11. ccl_chromium_reader/ccl_shared_proto_db_downloads.py +189 -0
  12. ccl_chromium_reader/common.py +19 -0
  13. ccl_chromium_reader/download_common.py +78 -0
  14. ccl_chromium_reader/profile_folder_protocols.py +276 -0
  15. ccl_chromium_reader/serialization_formats/__init__.py +0 -0
  16. ccl_chromium_reader/serialization_formats/ccl_blink_value_deserializer.py +401 -0
  17. ccl_chromium_reader/serialization_formats/ccl_easy_chromium_pickle.py +133 -0
  18. ccl_chromium_reader/serialization_formats/ccl_protobuff.py +276 -0
  19. ccl_chromium_reader/serialization_formats/ccl_v8_value_deserializer.py +627 -0
  20. ccl_chromium_reader/storage_formats/__init__.py +0 -0
  21. ccl_chromium_reader/storage_formats/ccl_leveldb.py +582 -0
  22. ccl_simplesnappy/__init__.py +1 -0
  23. ccl_simplesnappy/ccl_simplesnappy.py +306 -0
  24. linear_mcp_fast/__init__.py +8 -0
  25. linear_mcp_fast/__main__.py +6 -0
  26. linear_mcp_fast/reader.py +433 -0
  27. linear_mcp_fast/server.py +367 -0
  28. linear_mcp_fast/store_detector.py +117 -0
  29. linear_mcp_fast-0.1.0.dist-info/METADATA +160 -0
  30. linear_mcp_fast-0.1.0.dist-info/RECORD +39 -0
  31. linear_mcp_fast-0.1.0.dist-info/WHEEL +5 -0
  32. linear_mcp_fast-0.1.0.dist-info/entry_points.txt +2 -0
  33. linear_mcp_fast-0.1.0.dist-info/top_level.txt +4 -0
  34. tools_and_utilities/Chromium_dump_local_storage.py +111 -0
  35. tools_and_utilities/Chromium_dump_session_storage.py +92 -0
  36. tools_and_utilities/benchmark.py +35 -0
  37. tools_and_utilities/ccl_chrome_audit.py +651 -0
  38. tools_and_utilities/dump_indexeddb_details.py +59 -0
  39. tools_and_utilities/dump_leveldb.py +53 -0
@@ -0,0 +1,401 @@
1
+ """
2
+ Copyright 2020, CCL Forensics
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
21
+ """
22
+
23
+ import sys
24
+ import enum
25
+ import typing
26
+ from dataclasses import dataclass
27
+
28
+ from . import ccl_v8_value_deserializer
29
+
30
+ # See: https://chromium.googlesource.com/chromium/src/third_party/+/master/blink/renderer/bindings/core/v8/serialization
31
+ # https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
32
+
33
+
34
+ # WebCoreStrings are read as (length:uint32_t, string:UTF8[length]).
35
+ # RawStrings are read as (length:uint32_t, string:UTF8[length]).
36
+ # RawUCharStrings are read as
37
+ # (length:uint32_t, string:UChar[length/sizeof(UChar)]).
38
+ # RawFiles are read as
39
+ # (path:WebCoreString, url:WebCoreStrng, type:WebCoreString).
40
+ # There is a reference table that maps object references (uint32_t) to
41
+ # v8::Values.
42
+ # Tokens marked with (ref) are inserted into the reference table and given the
43
+ # next object reference ID after decoding.
44
+ # All tags except InvalidTag, PaddingTag, ReferenceCountTag, VersionTag,
45
+ # GenerateFreshObjectTag and GenerateFreshArrayTag push their results to the
46
+ # deserialization stack.
47
+ # There is also an 'open' stack that is used to resolve circular references.
48
+ # Objects or arrays may contain self-references. Before we begin to deserialize
49
+ # the contents of these values, they are first given object reference IDs (by
50
+ # GenerateFreshObjectTag/GenerateFreshArrayTag); these reference IDs are then
51
+ # used with ObjectReferenceTag to tie the recursive knot.
52
+
53
+ __version__ = "0.3"
54
+ __description__ = "Partial reimplementation of the Blink Javascript Object Serialization"
55
+ __contact__ = "Alex Caithness"
56
+
57
+ __DEBUG = False
58
+
59
+
60
+ def log(msg, debug_only=True):
61
+ if __DEBUG or not debug_only:
62
+ caller_name = sys._getframe(1).f_code.co_name
63
+ caller_line = sys._getframe(1).f_code.co_firstlineno
64
+ print(f"{caller_name} ({caller_line}):\t{msg}")
65
+
66
+
67
+ class BlobIndexType(enum.Enum):
68
+ Blob = 0
69
+ File = 1
70
+
71
+
72
+ @dataclass
73
+ class BlobIndex:
74
+ index_type: BlobIndexType
75
+ index_id: int
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class NativeFileHandle:
80
+ is_dir: bool
81
+ name: str
82
+ token_index: int
83
+
84
+
85
+ @dataclass(frozen=True)
86
+ class CryptoKey:
87
+ sub_type: "V8CryptoKeySubType"
88
+ algorithm_type: typing.Optional["V8CryptoKeyAlgorithm"]
89
+ hash_type: typing.Optional["V8CryptoKeyAlgorithm"]
90
+ asymmetric_key_type: typing.Optional["V8AsymmetricCryptoKeyType"]
91
+ byte_length: typing.Optional[int]
92
+ public_exponent: typing.Optional[bytes]
93
+ named_curve_type: typing.Optional["V8CryptoNamedCurve"]
94
+ key_usage: "V8CryptoKeyUsage"
95
+ key_data: bytes
96
+
97
+
98
+ class Constants:
99
+ tag_kMessagePortTag = b"M" # index:int -> MessagePort. Fills the result with
100
+ # transferred MessagePort.
101
+ tag_kMojoHandleTag = b"h" # index:int -> MojoHandle. Fills the result with
102
+ # transferred MojoHandle.
103
+ tag_kBlobTag = b"b" # uuid:WebCoreString, type:WebCoreString, size:uint64_t ->
104
+ # Blob (ref)
105
+ tag_kBlobIndexTag = b"i" # index:int32_t -> Blob (ref)
106
+ tag_kFileTag = b"f" # file:RawFile -> File (ref)
107
+ tag_kFileIndexTag = b"e" # index:int32_t -> File (ref)
108
+ tag_kDOMFileSystemTag = b"d" # type : int32_t, name:WebCoreString,
109
+ # uuid:WebCoreString -> FileSystem (ref)
110
+ tag_kNativeFileSystemFileHandleTag = b"n" # name:WebCoreString, index:uint32_t
111
+ # -> NativeFileSystemFileHandle (ref)
112
+ tag_kNativeFileSystemDirectoryHandleTag = b"N" # name:WebCoreString, index:uint32_t ->
113
+ # NativeFileSystemDirectoryHandle (ref)
114
+ tag_kFileListTag = b"l" # length:uint32_t, files:RawFile[length] -> FileList (ref)
115
+ tag_kFileListIndexTag = b"L" # length:uint32_t, files:int32_t[length] -> FileList (ref)
116
+ tag_kImageDataTag = b"#" # tags terminated by ImageSerializationTag::kEnd (see
117
+ # SerializedColorParams.h), width:uint32_t,
118
+ # height:uint32_t, pixelDataLength:uint64_t,
119
+ # data:byte[pixelDataLength]
120
+ # -> ImageData (ref)
121
+ tag_kImageBitmapTag = b"g" # tags terminated by ImageSerializationTag::kEnd (see
122
+ # SerializedColorParams.h), width:uint32_t,
123
+ # height:uint32_t, pixelDataLength:uint32_t,
124
+ # data:byte[pixelDataLength]
125
+ # -> ImageBitmap (ref)
126
+ tag_kImageBitmapTransferTag = b"G" # index:uint32_t -> ImageBitmap. For ImageBitmap transfer
127
+ tag_kOffscreenCanvasTransferTag = b"H" # index, width, height, id,
128
+ # filter_quality::uint32_t ->
129
+ # OffscreenCanvas. For OffscreenCanvas
130
+ # transfer
131
+ tag_kReadableStreamTransferTag = b"r" # index:uint32_t
132
+ tag_kTransformStreamTransferTag = b"m" # index:uint32_t
133
+ tag_kWritableStreamTransferTag = b"w" # index:uint32_t
134
+ tag_kDOMPointTag = b"Q" # x:Double, y:Double, z:Double, w:Double
135
+ tag_kDOMPointReadOnlyTag = b"W" # x:Double, y:Double, z:Double, w:Double
136
+ tag_kDOMRectTag = b"E" # x:Double, y:Double, width:Double, height:Double
137
+ tag_kDOMRectReadOnlyTag = b"R" # x:Double, y:Double, width:Double, height:Double
138
+ tag_kDOMQuadTag = b"T" # p1:Double, p2:Double, p3:Double, p4:Double
139
+ tag_kDOMMatrixTag = b"Y" # m11..m44: 16 Double
140
+ tag_kDOMMatrixReadOnlyTag = b"U" # m11..m44: 16 Double
141
+ tag_kDOMMatrix2DTag = b"I" # a..f: 6 Double
142
+ tag_kDOMMatrix2DReadOnlyTag = b"O" # a..f: 6 Double
143
+ tag_kCryptoKeyTag = b"K" # subtag:byte, props, usages:uint32_t,
144
+ # keyDataLength:uint32_t, keyData:byte[keyDataLength]
145
+ # If subtag=AesKeyTag:
146
+ # props = keyLengthBytes:uint32_t, algorithmId:uint32_t
147
+ # If subtag=HmacKeyTag:
148
+ # props = keyLengthBytes:uint32_t, hashId:uint32_t
149
+ # If subtag=RsaHashedKeyTag:
150
+ # props = algorithmId:uint32_t, type:uint32_t,
151
+ # modulusLengthBits:uint32_t,
152
+ # publicExponentLength:uint32_t,
153
+ # publicExponent:byte[publicExponentLength],
154
+ # hashId:uint32_t
155
+ # If subtag=EcKeyTag:
156
+ # props = algorithmId:uint32_t, type:uint32_t,
157
+ # namedCurve:uint32_t
158
+ tag_kRTCCertificateTag = b"k" # length:uint32_t, pemPrivateKey:WebCoreString,
159
+ # pemCertificate:WebCoreString
160
+ tag_kRTCEncodedAudioFrameTag = b"A" # uint32_t -> transferred audio frame ID
161
+ tag_kRTCEncodedVideoFrameTag = b"V" # uint32_t -> transferred video frame ID
162
+ tag_kVideoFrameTag = b"v" # uint32_t -> transferred video frame ID
163
+
164
+ # The following tags were used by the Shape Detection API implementation
165
+ # between M71 and M81. During these milestones, the API was always behind
166
+ # a flag. Usage was removed in https:#crrev.com/c/2040378.
167
+ tag_kDeprecatedDetectedBarcodeTag = b"B"
168
+ tag_kDeprecatedDetectedFaceTag = b"F"
169
+ tag_kDeprecatedDetectedTextTag = b"t"
170
+
171
+ tag_kDOMExceptionTag = b"x" # name:String,message:String,stack:String
172
+ tag_kVersionTag = b"\xff" # version:uint32_t -> Uses this as the file version.
173
+ tag_kTrailerOffsetTag = b"\xfe" # offset:uint64_t (fixed width, network order) from buffer, start size:uint32_t (fixed width, network order)
174
+ tag_kTrailerRequiresInterfacesTag = b"\xA0"
175
+
176
+
177
+ class V8CryptoKeySubType(enum.IntEnum):
178
+ """
179
+ See: third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h
180
+ Used by the kCryptoKeyTag type
181
+ """
182
+ AesKey = 1
183
+ HmacKey = 2
184
+ # ID 3 was used by RsaKeyTag, while still behind experimental flag.
185
+ RsaHashedKey = 4
186
+ EcKey = 5
187
+ NoParamsKey = 6
188
+
189
+
190
+ class V8CryptoKeyAlgorithm(enum.IntEnum):
191
+ """
192
+ See: third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h
193
+ Used by the kCryptoKeyTag type
194
+ """
195
+ AesCbcTag = 1
196
+ HmacTag = 2
197
+ RsaSsaPkcs1v1_5Tag = 3
198
+ # ID 4 was used by RsaEs, while still behind experimental flag.
199
+ Sha1Tag = 5
200
+ Sha256Tag = 6
201
+ Sha384Tag = 7
202
+ Sha512Tag = 8
203
+ AesGcmTag = 9
204
+ RsaOaepTag = 10
205
+ AesCtrTag = 11
206
+ AesKwTag = 12
207
+ RsaPssTag = 13
208
+ EcdsaTag = 14
209
+ EcdhTag = 15
210
+ HkdfTag = 16
211
+ Pbkdf2Tag = 17
212
+
213
+
214
+ class V8AsymmetricCryptoKeyType(enum.IntEnum):
215
+ Public = 1
216
+ Private = 2
217
+
218
+
219
+ class V8CryptoNamedCurve(enum.IntEnum):
220
+ """
221
+ See: third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h
222
+ Used by the kCryptoKeyTag type
223
+ """
224
+ P256 = 1
225
+ P384 = 2
226
+ P521 = 3
227
+
228
+
229
+ class V8CryptoKeyUsage(enum.IntFlag):
230
+ """
231
+ See: third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h
232
+ Used by the kCryptoKeyTag type
233
+ """
234
+ kExtractableUsage = 1 << 0
235
+ kEncryptUsage = 1 << 1
236
+ kDecryptUsage = 1 << 2
237
+ kSignUsage = 1 << 3
238
+ kVerifyUsage = 1 << 4
239
+ kDeriveKeyUsage = 1 << 5
240
+ kWrapKeyUsage = 1 << 6
241
+ kUnwrapKeyUsage = 1 << 7
242
+ kDeriveBitsUsage = 1 << 8
243
+
244
+
245
+ class BlinkV8Deserializer:
246
+ def _read_varint(self, stream) -> int:
247
+ return ccl_v8_value_deserializer.read_le_varint(stream)[0]
248
+
249
+ def _read_varint32(self, stream) -> int:
250
+ return ccl_v8_value_deserializer.read_le_varint(stream, is_32bit=True)[0]
251
+
252
+ def _read_utf8_string(self, stream: typing.BinaryIO) -> str:
253
+ length = self._read_varint32(stream)
254
+ raw_string = stream.read(length)
255
+ if len(raw_string) != length:
256
+ raise ValueError("Could not read all of the utf-8 data")
257
+ return raw_string.decode("utf-8")
258
+
259
+ # def _read_uint32(self, stream: typing.BinaryIO) -> int:
260
+ # raw = stream.read(4)
261
+ # if len(raw) < 4:
262
+ # raise ValueError("Could not read enough data when reading int32")
263
+ # return struct.unpack("<I", raw)[0]
264
+
265
+ def _read_file_index(self, stream: typing.BinaryIO) -> BlobIndex:
266
+ return BlobIndex(BlobIndexType.File, self._read_varint(stream))
267
+
268
+ def _read_blob_index(self, stream: typing.BinaryIO) -> BlobIndex:
269
+ return BlobIndex(BlobIndexType.Blob, self._read_varint(stream))
270
+
271
+ def _read_file_list_index(self, stream: typing.BinaryIO) -> typing.Iterable[BlobIndex]:
272
+ length = self._read_varint(stream)
273
+ result = [self._read_file_index(stream) for _ in range(length)]
274
+ return result
275
+
276
+ def _read_native_file_handle(self, is_dir: bool, stream: typing.BinaryIO) -> NativeFileHandle:
277
+ return NativeFileHandle(is_dir, self._read_utf8_string(stream), self._read_varint(stream))
278
+
279
+ def _read_crypto_key(self, stream: typing.BinaryIO):
280
+ sub_type = V8CryptoKeySubType(stream.read(1)[0])
281
+
282
+ if sub_type == V8CryptoKeySubType.AesKey:
283
+ algorithm_id = V8CryptoKeyAlgorithm(self._read_varint32(stream))
284
+ byte_length = self._read_varint32(stream)
285
+ params = {
286
+ "algorithm_type": algorithm_id,
287
+ "byte_length": byte_length,
288
+ "hash_type": None,
289
+ "named_curve_type": None,
290
+ "asymmetric_key_type": None,
291
+ "public_exponent": None
292
+ }
293
+ elif sub_type == V8CryptoKeySubType.HmacKey:
294
+ byte_length = self._read_varint32(stream)
295
+ hash_id = V8CryptoKeyAlgorithm(self._read_varint32(stream))
296
+ params = {
297
+ "byte_length": byte_length,
298
+ "hash_type": hash_id,
299
+ "algorithm_type": None,
300
+ "named_curve_type": None,
301
+ "asymmetric_key_type": None,
302
+ "public_exponent": None
303
+ }
304
+ elif sub_type == V8CryptoKeySubType.RsaHashedKey:
305
+ algorithm_id = V8CryptoKeyAlgorithm(self._read_varint32(stream))
306
+ asymmetric_key_type = V8AsymmetricCryptoKeyType(stream.read(1)[0])
307
+ length_bytes = self._read_varint32(stream)
308
+ public_exponent_length = self._read_varint32(stream)
309
+ public_exponent = stream.read(public_exponent_length)
310
+ if len(public_exponent) != public_exponent_length:
311
+ raise ValueError(f"Could not read all of public exponent data")
312
+ hash_id = V8CryptoKeyAlgorithm(self._read_varint32(stream))
313
+ params = {
314
+ "algorithm_type": algorithm_id,
315
+ "asymmetric_key_type": asymmetric_key_type,
316
+ "byte_length": length_bytes,
317
+ "public_exponent": public_exponent,
318
+ "hash_type": hash_id,
319
+ "named_curve_type": None
320
+ }
321
+
322
+ elif sub_type == V8CryptoKeySubType.EcKey:
323
+ algorithm_id = V8CryptoKeyAlgorithm(self._read_varint32(stream))
324
+ asymmetric_key_type = V8AsymmetricCryptoKeyType(stream.read(1)[0])
325
+ named_curve = V8CryptoNamedCurve(self._read_varint32(stream))
326
+ params = {
327
+ "algorithm_type": algorithm_id,
328
+ "asymmetric_key_type": asymmetric_key_type,
329
+ "named_curve_type": named_curve,
330
+ "hash_type": None,
331
+ "byte_length": None,
332
+ "public_exponent": None
333
+ }
334
+ elif sub_type == V8CryptoKeySubType.NoParamsKey:
335
+ algorithm_id = V8CryptoKeyAlgorithm(self._read_varint32(stream))
336
+ params = {
337
+ "algorithm_type": algorithm_id,
338
+ "hash_type": None,
339
+ "asymmetric_key_type": None,
340
+ "byte_length": None,
341
+ "named_curve_type": None,
342
+ "public_exponent": None
343
+ }
344
+ else:
345
+ raise ValueError(f"Unknown V8CryptoKeySubType {sub_type}")
346
+
347
+ params["key_usage"] = V8CryptoKeyUsage(self._read_varint32(stream))
348
+ key_length = self._read_varint32(stream)
349
+ key_data = stream.read(key_length)
350
+ if len(key_data) < key_length:
351
+ raise ValueError("Could not read all key data")
352
+
353
+ params["key_data"] = key_data
354
+ return CryptoKey(sub_type, **params)
355
+
356
+ def _not_implemented(self, stream):
357
+ raise NotImplementedError()
358
+
359
+ def read(self, stream: typing.BinaryIO) -> typing.Any:
360
+ tag = stream.read(1)
361
+
362
+ func = {
363
+ Constants.tag_kMessagePortTag: lambda x: self._not_implemented(x),
364
+ Constants.tag_kMojoHandleTag: lambda x: self._not_implemented(x),
365
+ Constants.tag_kBlobTag: lambda x: self._not_implemented(x),
366
+ Constants.tag_kBlobIndexTag: lambda x: self._read_blob_index(x),
367
+ Constants.tag_kFileTag: lambda x: self._not_implemented(x),
368
+ Constants.tag_kFileIndexTag: lambda x: self._read_file_index(x),
369
+ Constants.tag_kDOMFileSystemTag: lambda x: self._not_implemented(x),
370
+ Constants.tag_kNativeFileSystemFileHandleTag: lambda x: self._read_native_file_handle(False, x),
371
+ Constants.tag_kNativeFileSystemDirectoryHandleTag: lambda x: self._read_native_file_handle(True, x),
372
+ Constants.tag_kFileListTag: lambda x: self._not_implemented(x),
373
+ Constants.tag_kFileListIndexTag: lambda x: self._read_file_list_index(x),
374
+ Constants.tag_kImageDataTag: lambda x: self._not_implemented(x),
375
+ Constants.tag_kImageBitmapTag: lambda x: self._not_implemented(x),
376
+ Constants.tag_kImageBitmapTransferTag: lambda x: self._not_implemented(x),
377
+ Constants.tag_kOffscreenCanvasTransferTag: lambda x: self._not_implemented(x),
378
+ Constants.tag_kReadableStreamTransferTag: lambda x: self._not_implemented(x),
379
+ Constants.tag_kTransformStreamTransferTag: lambda x: self._not_implemented(x),
380
+ Constants.tag_kWritableStreamTransferTag: lambda x: self._not_implemented(x),
381
+ Constants.tag_kDOMPointTag: lambda x: self._not_implemented(x),
382
+ Constants.tag_kDOMPointReadOnlyTag: lambda x: self._not_implemented(x),
383
+ Constants.tag_kDOMRectTag: lambda x: self._not_implemented(x),
384
+ Constants.tag_kDOMRectReadOnlyTag: lambda x: self._not_implemented(x),
385
+ Constants.tag_kDOMQuadTag: lambda x: self._not_implemented(x),
386
+ Constants.tag_kDOMMatrixTag: lambda x: self._not_implemented(x),
387
+ Constants.tag_kDOMMatrixReadOnlyTag: lambda x: self._not_implemented(x),
388
+ Constants.tag_kDOMMatrix2DTag: lambda x: self._not_implemented(x),
389
+ Constants.tag_kDOMMatrix2DReadOnlyTag: lambda x: self._not_implemented(x),
390
+ Constants.tag_kCryptoKeyTag: lambda x: self._read_crypto_key(x),
391
+ Constants.tag_kRTCCertificateTag: lambda x: self._not_implemented(x),
392
+ Constants.tag_kRTCEncodedAudioFrameTag: lambda x: self._not_implemented(x),
393
+ Constants.tag_kRTCEncodedVideoFrameTag: lambda x: self._not_implemented(x),
394
+ Constants.tag_kVideoFrameTag: lambda x: self._not_implemented(x),
395
+ Constants.tag_kDOMExceptionTag: lambda x: self._not_implemented(x)
396
+ }.get(tag)
397
+
398
+ if func is None:
399
+ raise ValueError(f"Unknown tag: {tag}")
400
+
401
+ return func(stream)
@@ -0,0 +1,133 @@
1
+ """
2
+ Copyright 2022, CCL Forensics
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
21
+ """
22
+
23
+ import io
24
+ import datetime
25
+ import struct
26
+ import os
27
+
28
+
29
+ __version__ = "0.1"
30
+ __description__ = "Module for reading Chromium Pickles."
31
+ __contact__ = "Alex Caithness"
32
+
33
+
34
+ class EasyPickleException(Exception):
35
+ ...
36
+
37
+
38
+ class EasyPickleIterator:
39
+ """
40
+ A pythonic implementation of the PickleIterator object used in various places in Chrom(e|ium).
41
+ """
42
+ def __init__(self, data: bytes, alignment: int=4):
43
+ """
44
+ Takes a bytes buffer and wraps the EasyPickleIterator around it
45
+ :param data: the data to be wrapped
46
+ :param alignment: (optional) the number of bytes to align reads to (default: 4)
47
+ """
48
+ self._f = io.BytesIO(data)
49
+ self._alignment = alignment
50
+
51
+ self._pickle_length = self.read_uint32()
52
+ if len(data) != self._pickle_length + 4:
53
+ raise EasyPickleException("pickle length invalid")
54
+
55
+ def __enter__(self) -> "EasyPickleIterator":
56
+ return self
57
+
58
+ def __exit__(self, exc_type, exc_val, exc_tb):
59
+ self.close()
60
+
61
+ def close(self):
62
+ self._f.close()
63
+
64
+ def read_aligned(self, length: int) -> bytes:
65
+ """
66
+ reads the number of bytes specified by the length parameter. Aligns the buffer afterwards if required.
67
+ :param length: the length od data to be read
68
+ :return: the data read (without the alignment padding)
69
+ """
70
+ raw = self._f.read(length)
71
+ if len(raw) != length:
72
+ raise EasyPickleException(f"Tried to read {length} bytes but only got {len(raw)}")
73
+
74
+ align_count = self._alignment - (length % self._alignment)
75
+ if align_count != self._alignment:
76
+ self._f.seek(align_count, os.SEEK_CUR)
77
+
78
+ return raw
79
+
80
+ def read_uint16(self) -> int:
81
+ raw = self.read_aligned(2)
82
+ return struct.unpack("<H", raw)[0]
83
+
84
+ def read_uint32(self) -> int:
85
+ raw = self.read_aligned(4)
86
+ return struct.unpack("<I", raw)[0]
87
+
88
+ def read_uint64(self) -> int:
89
+ raw = self.read_aligned(8)
90
+ return struct.unpack("<Q", raw)[0]
91
+
92
+ def read_int16(self) -> int:
93
+ raw = self.read_aligned(2)
94
+ return struct.unpack("<h", raw)[0]
95
+
96
+ def read_int32(self) -> int:
97
+ raw = self.read_aligned(4)
98
+ return struct.unpack("<i", raw)[0]
99
+
100
+ def read_int64(self) -> int:
101
+ raw = self.read_aligned(8)
102
+ return struct.unpack("<q", raw)[0]
103
+
104
+ def read_bool(self) -> bool:
105
+ raw = self.read_int32()
106
+ if raw == 0:
107
+ return False
108
+ elif raw == 1:
109
+ return True
110
+ else:
111
+ raise EasyPickleException("bools should only contain 0 or 1")
112
+
113
+ def read_single(self) -> float:
114
+ raw = self.read_aligned(4)
115
+ return struct.unpack("<f", raw)[0]
116
+
117
+ def read_double(self) -> float:
118
+ raw = self.read_aligned(8)
119
+ return struct.unpack("<d", raw)[0]
120
+
121
+ def read_string(self) -> str:
122
+ length = self.read_uint32()
123
+ raw = self.read_aligned(length)
124
+ return raw.decode("utf-8")
125
+
126
+ def read_string16(self) -> str:
127
+ length = self.read_uint32() * 2 # character count
128
+ raw = self.read_aligned(length)
129
+ return raw.decode("utf-16-le")
130
+
131
+ def read_datetime(self) -> datetime.datetime:
132
+ return datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=self.read_uint64())
133
+