binarycookies 2.2.3__tar.gz → 2.3.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: binarycookies
3
- Version: 2.2.3
3
+ Version: 2.3.0
4
4
  Summary: Python Binary Cookies (de)serializer
5
5
  Author: Daniel Tom
6
6
  Author-email: d.e.tom89@gmail.com
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Requires-Dist: pydantic (>=2.0.0,<3.0.0)
14
- Requires-Dist: typer (>=0.12.3,<0.20.0)
14
+ Requires-Dist: typer (>=0.12.3,<0.21.0)
15
15
  Description-Content-Type: text/markdown
16
16
 
17
17
  [![Github Actions Status](https://github.com/dan1elt0m/binary-cookies-reader/workflows/test/badge.svg)](https://github.com/dan1elt0m/binary-cookies-reader/actions/workflows/test.yml)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "binarycookies"
3
- version = "2.2.3"
3
+ version = "2.3.0"
4
4
  description = "Python Binary Cookies (de)serializer"
5
5
  authors = ["Daniel Tom <d.e.tom89@gmail.com>"]
6
6
  readme = "README.md"
@@ -8,7 +8,7 @@ packages = [{include = "binarycookies", from="src"}]
8
8
 
9
9
  [tool.poetry.dependencies]
10
10
  python = ">=3.8,<4.0"
11
- typer = ">=0.12.3,<0.20.0"
11
+ typer = ">=0.12.3,<0.21.0"
12
12
  pydantic = ">=2.0.0,<3.0.0"
13
13
 
14
14
  [tool.poetry.group.dev.dependencies]
@@ -11,14 +11,14 @@ package_data = \
11
11
  {'': ['*']}
12
12
 
13
13
  install_requires = \
14
- ['pydantic>=2.0.0,<3.0.0', 'typer>=0.12.3,<0.20.0']
14
+ ['pydantic>=2.0.0,<3.0.0', 'typer>=0.12.3,<0.21.0']
15
15
 
16
16
  entry_points = \
17
17
  {'console_scripts': ['bcparser = binarycookies.__main__:main']}
18
18
 
19
19
  setup_kwargs = {
20
20
  'name': 'binarycookies',
21
- 'version': '2.2.3',
21
+ 'version': '2.3.0',
22
22
  'description': 'Python Binary Cookies (de)serializer',
23
23
  'long_description': '[![Github Actions Status](https://github.com/dan1elt0m/binary-cookies-reader/workflows/test/badge.svg)](https://github.com/dan1elt0m/binary-cookies-reader/actions/workflows/test.yml)\n<h1>\n <img src="docs/bincook.png" width="30" style="vertical-align: middle; margin-right: 10px;">\n Binary Cookies\n</h1>\n\nCLI tool and Python library for reading and writing Binary Cookies.\n\n### Documentation\nFor detailed documentation, please visit the [Binary Cookies Documentation](https://dan1elt0m.github.io/binarycookies/)\n\n### Requirements\n\n- Python >= 3.9\n\n### Installation\n```bash \npip install binarycookies\n```\n\n### CLI example:\n```sh\nbcparser path/to/cookies.binarycookies\n```\n\nOutput:\n```json\n[\n {\n "name": "session_id",\n "value": "abc123",\n "url": "https://example.com",\n "path": "/",\n "create_datetime": "2023-10-01T12:34:56+00:00",\n "expiry_datetime": "2023-12-31T23:59:59+00:00",\n "flag": "Secure"\n },\n {\n "name": "user_token",\n "value": "xyz789",\n "url": "https://example.com",\n "path": "/account",\n "create_datetime": "2023-10-01T12:34:56+00:00",\n "expiry_datetime": "2023-12-31T23:59:59+00:00",\n "flag": "HttpOnly"\n }\n]\n```\n#### Output formats\nThe CLI supports multiple output formats using the --output flag.\n- `json` (default): Outputs cookies in JSON format.\n- `ascii`: Outputs cookies in a human-readable ASCII format with each cookie property on a separate line.\n- `netscape`: Outputs cookies in the Netscape cookie file format.\n\n### Basic Usage Python\n\n#### Deserialization\n\n```python\nimport binarycookies \n\nwith open("path/to/cookies.binarycookies", "rb") as f:\n cookies = binarycookies.load(f)\n```\n\n#### Serialization\n\n```python\nimport binarycookies \n\ncookie = {\n "name": "session_id",\n "value": "abc123",\n "url": "https://example.com",\n "path": "/",\n "create_datetime": "2023-10-01T12:34:56+00:00",\n "expiry_datetime": "2023-12-31T23:59:59+00:00",\n "flag": "Secure"\n}\n\nwith open("path/to/cookies.binarycookies", "wb") as f:\n binarycookies.dump(cookie, f)\n```\n\n### Ethical Use & Responsible Handling\nThis project is intended for lawful, ethical use only. Typical, appropriate uses include:\n- Inspecting Binary Cookies from your own devices or data you are authorized to access\n- DFIR, QA, and security testing performed with explicit, written permission\n- Educational/research work on datasets that are owned by you, anonymized, or publicly released for that purpose\n\nYou must not use this tool to:\n- Access, extract, modify, or distribute cookies from systems or accounts you do not own or have permission to analyze\n- Bypass authentication, session management, DRM, or other technical controls\n- Enable tracking, stalking, doxxing, fraud, or other privacy-invasive or harmful activities\n\n\n### License\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n\n### Contributing\nContributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. Pull requests are also welcome.\n',
24
24
  'author': 'Daniel Tom',
@@ -0,0 +1,12 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ from binarycookies._deserialize import load, loads
4
+ from binarycookies._serialize import dump, dumps
5
+
6
+ try:
7
+ __version__ = version("binarycookies")
8
+ except PackageNotFoundError:
9
+ # Package is not installed, fallback to a default
10
+ __version__ = "0.0.0+unknown"
11
+
12
+ __all__ = ["dump", "dumps", "load", "loads"]
@@ -1,11 +1,11 @@
1
1
  import json
2
2
  from datetime import datetime
3
- from typing import Type
3
+ from typing import Optional, Type
4
4
 
5
5
  import typer
6
6
  from rich import print as rprint
7
7
 
8
- from binarycookies import load
8
+ from binarycookies import __version__, load
9
9
  from binarycookies._output_handlers import OUTPUT_HANDLERS, OutputType
10
10
 
11
11
 
@@ -16,7 +16,24 @@ class DateTimeEncoder(json.JSONEncoder):
16
16
  return super().default(obj)
17
17
 
18
18
 
19
- def cli(file_path: str, output: OutputType = OutputType.json):
19
+ app = typer.Typer()
20
+
21
+
22
+ def version_callback(value: bool): # noqa: FBT001
23
+ """Callback to print version and exit."""
24
+ if value:
25
+ rprint(f"binarycookies version {__version__}")
26
+ raise typer.Exit
27
+
28
+
29
+ @app.command()
30
+ def cli(
31
+ file_path: str = typer.Argument(..., help="Path to binary cookies file"),
32
+ output: OutputType = typer.Option(OutputType.json, "--output", "-o", help="Output format"),
33
+ version: Optional[bool] = typer.Option( # noqa: ARG001
34
+ None, "--version", "-v", callback=version_callback, is_eager=True, help="Show version and exit"
35
+ ),
36
+ ):
20
37
  """CLI entrypoint for reading Binary Cookies"""
21
38
  with open(file_path, "rb") as f:
22
39
  cookies = load(f)
@@ -31,7 +48,7 @@ def cli(file_path: str, output: OutputType = OutputType.json):
31
48
 
32
49
  def main():
33
50
  """CLI entrypoint for reading Binary Cookies"""
34
- typer.run(cli)
51
+ app()
35
52
 
36
53
 
37
54
  if __name__ == "__main__":
@@ -86,6 +86,10 @@ def read_cookie(cookie: BytesIO, cookie_size: int) -> Cookie:
86
86
  flag_int = read_field(cookie, cookie_fields.flag)
87
87
  flag = interpret_flag(flag_int)
88
88
 
89
+ # Read comment offset at offset 32
90
+ cookie.seek(32)
91
+ comment_offset = unpack(Format.integer, cookie.read(4))[0] # noqa: F841
92
+
89
93
  url_offset = read_field(cookie, cookie_fields.url_offset)
90
94
  name_offset = read_field(cookie, cookie_fields.name_offset)
91
95
  path_offset = read_field(cookie, cookie_fields.path_offset)
@@ -94,6 +98,7 @@ def read_cookie(cookie: BytesIO, cookie_size: int) -> Cookie:
94
98
  expiry_datetime = mac_epoch_to_date(read_field(cookie, cookie_fields.expiry_date))
95
99
  create_datetime = mac_epoch_to_date(read_field(cookie, cookie_fields.create_date))
96
100
 
101
+ # Read strings - comment comes first, then domain (url)
97
102
  url = read_field(cookie, BcField(offset=url_offset, size=name_offset - url_offset, format=Format.string))
98
103
  name = read_field(cookie, BcField(offset=name_offset, size=path_offset - name_offset, format=Format.string))
99
104
  path = read_field(cookie, BcField(offset=path_offset, size=value_offset - path_offset, format=Format.string))
@@ -0,0 +1,222 @@
1
+ from datetime import datetime, timezone
2
+ from io import BufferedWriter, BytesIO
3
+ from struct import pack
4
+ from typing import BinaryIO, Dict, List, Tuple, Union
5
+
6
+ from pydantic import __version__ as pydantic_version
7
+
8
+ from binarycookies._deserialize import FLAGS
9
+ from binarycookies.models import BcField, Cookie, CookieFields, FileFields, Format
10
+
11
+ IS_PYDANTIC_V1 = pydantic_version.startswith("1.")
12
+
13
+ CookiesCollection = Union[List[Dict], List[Cookie], Tuple[Dict], Tuple[Cookie], Cookie, Dict[str, str]]
14
+
15
+
16
+ def date_to_mac_epoch(date: datetime) -> int:
17
+ """Converts a datetime object to mac epoch time."""
18
+ mac_epoch_start = datetime(2001, 1, 1, tzinfo=timezone.utc)
19
+ return int((date - mac_epoch_start).total_seconds())
20
+
21
+
22
+ def write_string(data: BytesIO, value: str):
23
+ """Writes a string to binary file."""
24
+ data.write(value.encode() + b"\x00")
25
+
26
+
27
+ def write_field(data: BytesIO, field: BcField, value: Union[str, int]):
28
+ """Writes a field to binary data."""
29
+ data.seek(field.offset)
30
+ if field.format == Format.string:
31
+ write_string(data, value)
32
+ else:
33
+ data.write(pack(field.format, value))
34
+
35
+
36
+ def serialize_cookie(cookie: Cookie) -> bytes:
37
+ """Serializes a cookie object to binary format."""
38
+ cookie_fields = CookieFields()
39
+
40
+ # Pre-calculate the size to allocate buffer
41
+ # Cookie header is 60 bytes according to spec:
42
+ # 0-3: size, 4-7: unknownOne, 8-11: flags, 12-15: unknownTwo
43
+ # 16-19: domainOffset, 20-23: nameOffset, 24-27: pathOffset, 28-31: valueOffset
44
+ # 32-35: commentOffset, 36-39: endHeader
45
+ # 40-47: expires, 48-55: creation
46
+ # 56-59: comment (empty), 60+: domain, name, path, value strings
47
+ url_bytes = cookie.url.encode("utf-8")
48
+ name_bytes = cookie.name.encode("utf-8")
49
+ path_bytes = cookie.path.encode("utf-8")
50
+ value_bytes = cookie.value.encode("utf-8")
51
+ comment_bytes = b"" # Empty comment
52
+
53
+ # Each string has a null terminator
54
+ header_size = 60
55
+ strings_size = (
56
+ len(comment_bytes) + 1 + len(url_bytes) + 1 + len(name_bytes) + 1 + len(path_bytes) + 1 + len(value_bytes) + 1
57
+ )
58
+ total_size = header_size + strings_size
59
+
60
+ # Pre-allocate buffer with zeros
61
+ cookie_data = BytesIO(b"\x00" * total_size)
62
+
63
+ # Write flag
64
+ write_field(cookie_data, cookie_fields.flag, list(FLAGS.keys())[list(FLAGS.values()).index(cookie.flag)])
65
+
66
+ # Calculate offsets - strings start at byte 60 after header
67
+ comment_offset = 60
68
+ domain_offset = comment_offset + len(comment_bytes) + 1 # +1 for null terminator
69
+ name_offset = domain_offset + len(url_bytes) + 1
70
+ path_offset = name_offset + len(name_bytes) + 1
71
+ value_offset = path_offset + len(path_bytes) + 1
72
+
73
+ # Write offsets (note: spec calls it domain but code uses url)
74
+ write_field(cookie_data, cookie_fields.url_offset, domain_offset)
75
+ write_field(cookie_data, cookie_fields.name_offset, name_offset)
76
+ write_field(cookie_data, cookie_fields.path_offset, path_offset)
77
+ write_field(cookie_data, cookie_fields.value_offset, value_offset)
78
+
79
+ # Write commentOffset at offset 32
80
+ cookie_data.seek(32)
81
+ cookie_data.write(pack(Format.integer, comment_offset))
82
+
83
+ # Write endHeader marker at offset 36 (4 bytes of 0x00)
84
+ cookie_data.seek(36)
85
+ cookie_data.write(b"\x00\x00\x00\x00")
86
+
87
+ write_field(cookie_data, cookie_fields.expiry_date, date_to_mac_epoch(cookie.expiry_datetime))
88
+ write_field(cookie_data, cookie_fields.create_date, date_to_mac_epoch(cookie.create_datetime))
89
+
90
+ # Write string data starting at offset 60
91
+ cookie_data.seek(60)
92
+ # Write comment (empty string with null terminator)
93
+ write_string(cookie_data, "")
94
+ # Write domain (url), name, path, value
95
+ write_string(cookie_data, cookie.url)
96
+ write_string(cookie_data, cookie.name)
97
+ write_string(cookie_data, cookie.path)
98
+ write_string(cookie_data, cookie.value)
99
+
100
+ # Write size at the beginning
101
+ size = len(cookie_data.getvalue())
102
+ cookie_data.seek(0)
103
+ cookie_data.write(pack(Format.integer, size))
104
+ return cookie_data.getvalue()
105
+
106
+
107
+ def dump(cookies: CookiesCollection, f: Union[BufferedWriter, BytesIO, BinaryIO]):
108
+ """Dumps a Binary Cookies object to create a binary cookies file.
109
+
110
+ Args:
111
+ cookies: A Binary Cookies object to be serialized.
112
+ f: The file-like object to write the binary cookies data to.
113
+ """
114
+ binary = dumps(cookies)
115
+ f.write(binary)
116
+
117
+
118
+ def calculate_checksum(page_data: bytes) -> int:
119
+ """Calculates the checksum by summing every 4th byte of the page data.
120
+
121
+ Args:
122
+ page_data: The raw bytes of a page.
123
+ Returns:
124
+ int: The checksum value.
125
+ """
126
+ checksum = 0
127
+ # Sum every 4th byte (bytes at positions 0, 4, 8, 12, ...)
128
+ for i in range(0, len(page_data), 4):
129
+ checksum += page_data[i]
130
+ return checksum
131
+
132
+
133
+ def dumps(cookies: CookiesCollection) -> bytes:
134
+ """Dumps a Binary Cookies object to a byte string.
135
+
136
+ Args:
137
+ cookies: A Binary Cookies object to be serialized.
138
+ Returns:
139
+ bytes: The serialized binary cookies data.
140
+ """
141
+ if isinstance(cookies, dict):
142
+ cookies = [Cookie.parse_obj(cookies)] if IS_PYDANTIC_V1 else [Cookie.model_validate(cookies)]
143
+ elif isinstance(cookies, (list, tuple)):
144
+ if IS_PYDANTIC_V1:
145
+ cookies = [Cookie.parse_obj(cookie) for cookie in cookies]
146
+ else:
147
+ cookies = [Cookie.model_validate(cookie) for cookie in cookies]
148
+ elif isinstance(cookies, Cookie):
149
+ cookies = [cookies]
150
+ else:
151
+ raise TypeError("Invalid type for cookies. Expected dict, list, tuple, or Cookie.")
152
+
153
+ file_fields = FileFields()
154
+
155
+ data = BytesIO()
156
+
157
+ # Write file header (4 bytes: "cook")
158
+ data.write(b"cook")
159
+
160
+ # Number of pages (1 for simplicity, big-endian)
161
+ write_field(data, file_fields.num_pages, 1)
162
+
163
+ # Write page size pointer
164
+ data.write(pack(Format.integer, 0)) # Placeholder, will be updated
165
+
166
+ # Store the position where page data starts
167
+ page_start_offset = data.tell()
168
+ page_data = BytesIO()
169
+
170
+ # Write pageStart marker (4 bytes) - Must be 0x00, 0x01, 0x00, 0x00
171
+ page_data.write(b"\x00\x01\x00\x00")
172
+
173
+ # Write number of cookies in the page
174
+ page_data.write(pack(Format.integer, len(cookies)))
175
+
176
+ cookie_data_list = []
177
+ # Serialize cookies
178
+ for cookie in cookies:
179
+ cookie_data_list.append(serialize_cookie(cookie))
180
+
181
+ # Calculate where cookie data will start:
182
+ # current position + (num_cookies * 4 bytes for offsets) + 4 bytes for pageEnd marker
183
+ initial_cookie_offset = page_data.tell() + (len(cookies) * 4) + 4
184
+ initial_cookie = True
185
+ previous_sizes = 0
186
+
187
+ # Write cookie offsets
188
+ for cookie_data in cookie_data_list:
189
+ if initial_cookie:
190
+ page_data.write(pack(Format.integer, initial_cookie_offset))
191
+ initial_cookie = False
192
+ else:
193
+ page_data.write(pack(Format.integer, previous_sizes + initial_cookie_offset))
194
+
195
+ previous_sizes += len(cookie_data)
196
+
197
+ # Write pageEnd marker (4 bytes) - Must be 0x00, 0x00, 0x00, 0x00
198
+ page_data.write(b"\x00\x00\x00\x00")
199
+
200
+ # Write cookie data
201
+ for cookie_data in cookie_data_list:
202
+ page_data.write(cookie_data)
203
+
204
+ # Get the complete page data
205
+ page_bytes = page_data.getvalue()
206
+ page_size = len(page_bytes)
207
+
208
+ # Update page size in the file header (big-endian format for page sizes)
209
+ data.seek(8)
210
+ data.write(pack(Format.integer_be, page_size))
211
+
212
+ # Write the page data
213
+ data.seek(page_start_offset)
214
+ data.write(page_bytes)
215
+
216
+ # Calculate and write checksum after all pages
217
+ # The checksum is the sum of every 4th byte of the page data
218
+ # Specification says 8 bytes, so we write it as a 64-bit integer
219
+ checksum = calculate_checksum(page_bytes)
220
+ data.write(pack("<Q", checksum)) # LE_uint64 (8 bytes)
221
+
222
+ return data.getvalue()
@@ -1,4 +0,0 @@
1
- from binarycookies._deserialize import load, loads
2
- from binarycookies._serialize import dump, dumps
3
-
4
- __all__ = ["dump", "dumps", "load", "loads"]
@@ -1,150 +0,0 @@
1
- from datetime import datetime, timezone
2
- from io import BufferedWriter, BytesIO
3
- from struct import pack
4
- from typing import BinaryIO, Dict, List, Tuple, Union
5
-
6
- from pydantic import __version__ as pydantic_version
7
-
8
- from binarycookies._deserialize import FLAGS
9
- from binarycookies.models import BcField, Cookie, CookieFields, FileFields, Format
10
-
11
- IS_PYDANTIC_V1 = pydantic_version.startswith("1.")
12
-
13
- CookiesCollection = Union[List[Dict], List[Cookie], Tuple[Dict], Tuple[Cookie], Cookie, Dict[str, str]]
14
-
15
-
16
- def date_to_mac_epoch(date: datetime) -> int:
17
- """Converts a datetime object to mac epoch time."""
18
- mac_epoch_start = datetime(2001, 1, 1, tzinfo=timezone.utc)
19
- return int((date - mac_epoch_start).total_seconds())
20
-
21
-
22
- def write_string(data: BytesIO, value: str):
23
- """Writes a string to binary file."""
24
- data.write(value.encode() + b"\x00")
25
-
26
-
27
- def write_field(data: BytesIO, field: BcField, value: Union[str, int]):
28
- """Writes a field to binary data."""
29
- data.seek(field.offset)
30
- if field.format == Format.string:
31
- write_string(data, value)
32
- else:
33
- data.write(pack(field.format, value))
34
-
35
-
36
- def serialize_cookie(cookie: Cookie) -> bytes:
37
- """Serializes a cookie object to binary format."""
38
- cookie_data = BytesIO()
39
- cookie_fields = CookieFields()
40
- # Write flag
41
- write_field(cookie_data, cookie_fields.flag, list(FLAGS.keys())[list(FLAGS.values()).index(cookie.flag)])
42
-
43
- # Calculate offsets
44
- url_offset = 56 # The actual cookies content always starts at byte 56
45
- name_offset = 1 + url_offset + len(cookie.url.encode("utf-8"))
46
- path_offset = 1 + name_offset + len(cookie.name.encode("utf-8"))
47
- value_offset = 1 + path_offset + len(cookie.path.encode("utf-8"))
48
-
49
- write_field(cookie_data, cookie_fields.url_offset, url_offset)
50
- write_field(cookie_data, cookie_fields.name_offset, name_offset)
51
- write_field(cookie_data, cookie_fields.path_offset, path_offset)
52
- write_field(cookie_data, cookie_fields.value_offset, value_offset)
53
-
54
- write_field(cookie_data, cookie_fields.expiry_date, date_to_mac_epoch(cookie.expiry_datetime))
55
- write_field(cookie_data, cookie_fields.create_date, date_to_mac_epoch(cookie.create_datetime))
56
-
57
- # Write cookie data
58
- write_string(cookie_data, cookie.url)
59
- write_string(cookie_data, cookie.name)
60
- write_string(cookie_data, cookie.path)
61
- write_string(cookie_data, cookie.value)
62
-
63
- # Write size at the beginning
64
- size = len(cookie_data.getvalue())
65
- cookie_data.seek(0)
66
- cookie_data.write(pack(Format.integer, size))
67
- return cookie_data.getvalue()
68
-
69
-
70
- def dump(cookies: CookiesCollection, f: Union[BufferedWriter, BytesIO, BinaryIO]):
71
- """Dumps a Binary Cookies object to create a binary cookies file.
72
-
73
- Args:
74
- cookies: A Binary Cookies object to be serialized.
75
- f: The file-like object to write the binary cookies data to.
76
- """
77
- binary = dumps(cookies)
78
- f.write(binary)
79
-
80
-
81
- def dumps(cookies: CookiesCollection) -> bytes:
82
- """Dumps a Binary Cookies object to a byte string.
83
-
84
- Args:
85
- cookies: A Binary Cookies object to be serialized.
86
- Returns:
87
- bytes: The serialized binary cookies data.
88
- """
89
- if isinstance(cookies, dict):
90
- cookies = [Cookie.parse_obj(cookies)] if IS_PYDANTIC_V1 else [Cookie.model_validate(cookies)]
91
- elif isinstance(cookies, (list, tuple)):
92
- if IS_PYDANTIC_V1:
93
- cookies = [Cookie.parse_obj(cookie) for cookie in cookies]
94
- else:
95
- cookies = [Cookie.model_validate(cookie) for cookie in cookies]
96
- elif isinstance(cookies, Cookie):
97
- cookies = [cookies]
98
- else:
99
- raise TypeError("Invalid type for cookies. Expected dict, list, tuple, or Cookie.")
100
-
101
- file_fields = FileFields()
102
-
103
- data = BytesIO()
104
-
105
- # Write file header
106
- write_field(data, file_fields.header, "cook")
107
-
108
- # Number of pages (1 for simplicity)
109
- write_field(data, file_fields.num_pages, 1)
110
-
111
- # Write number of cookies
112
- data.write(pack(Format.integer, len(cookies)))
113
-
114
- # Placeholder for page size
115
- page_size_offset = data.tell()
116
- data.write(b"\x00\x00\x00\x00")
117
-
118
- # Write number of cookies
119
- data.write(pack(Format.integer, len(cookies)))
120
- cookie_data_list = []
121
- # Write cookies
122
- for cookie in cookies:
123
- cookie_data_list.append(serialize_cookie(cookie))
124
-
125
- initial_cookie_offset = data.tell() + (len(cookies) * 4)
126
- initial_cookie = True
127
- previous_sizes = 0
128
- for cookie_data in cookie_data_list:
129
- if initial_cookie:
130
- data.write(pack(Format.integer, initial_cookie_offset))
131
- initial_cookie = False
132
- else:
133
- data.write(pack(Format.integer, previous_sizes + initial_cookie_offset))
134
-
135
- previous_sizes += len(cookie_data)
136
-
137
- # Unknown data
138
- data.write(b"\x00\x00\x00\x00")
139
- data.write(b"\x00\x00\x00\x00")
140
- data.write(b"\x00\x00\x00\x00")
141
-
142
- for cookie_data in cookie_data_list:
143
- data.write(cookie_data)
144
-
145
- # Update page size
146
- page_size = data.tell()
147
- data.seek(page_size_offset)
148
- data.write(pack(Format.integer, page_size))
149
-
150
- return data.getvalue()
File without changes
File without changes