cronos-extract 1.0.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.
Files changed (41) hide show
  1. cronos_extract-1.0.0/LICENSE +22 -0
  2. cronos_extract-1.0.0/PKG-INFO +394 -0
  3. cronos_extract-1.0.0/README.md +367 -0
  4. cronos_extract-1.0.0/pyproject.toml +96 -0
  5. cronos_extract-1.0.0/pyproject.toml.orig +70 -0
  6. cronos_extract-1.0.0/src/cronos_extract/Database.py +374 -0
  7. cronos_extract-1.0.0/src/cronos_extract/Datafile.py +263 -0
  8. cronos_extract-1.0.0/src/cronos_extract/Datamodel.py +301 -0
  9. cronos_extract-1.0.0/src/cronos_extract/__init__.py +65 -0
  10. cronos_extract-1.0.0/src/cronos_extract/_api/__init__.py +2 -0
  11. cronos_extract-1.0.0/src/cronos_extract/_api/bank.py +439 -0
  12. cronos_extract-1.0.0/src/cronos_extract/_api/crack.py +151 -0
  13. cronos_extract-1.0.0/src/cronos_extract/_api/datafiles.py +120 -0
  14. cronos_extract-1.0.0/src/cronos_extract/_api/diagnostics.py +115 -0
  15. cronos_extract-1.0.0/src/cronos_extract/_api/errors.py +22 -0
  16. cronos_extract-1.0.0/src/cronos_extract/_api/info.py +79 -0
  17. cronos_extract-1.0.0/src/cronos_extract/_api/kod.py +54 -0
  18. cronos_extract-1.0.0/src/cronos_extract/_api/values.py +209 -0
  19. cronos_extract-1.0.0/src/cronos_extract/_cli/__init__.py +2 -0
  20. cronos_extract-1.0.0/src/cronos_extract/_cli/crack.py +417 -0
  21. cronos_extract-1.0.0/src/cronos_extract/_cli/csv_out.py +141 -0
  22. cronos_extract-1.0.0/src/cronos_extract/_cli/export.py +287 -0
  23. cronos_extract-1.0.0/src/cronos_extract/_cli/inspect.py +250 -0
  24. cronos_extract-1.0.0/src/cronos_extract/_cli/jsonl_out.py +83 -0
  25. cronos_extract-1.0.0/src/cronos_extract/_cli/names.py +58 -0
  26. cronos_extract-1.0.0/src/cronos_extract/_cli/options.py +73 -0
  27. cronos_extract-1.0.0/src/cronos_extract/_cli/report.py +201 -0
  28. cronos_extract-1.0.0/src/cronos_extract/_cli/sql_out.py +128 -0
  29. cronos_extract-1.0.0/src/cronos_extract/_diagnostic.py +59 -0
  30. cronos_extract-1.0.0/src/cronos_extract/_format/__init__.py +2 -0
  31. cronos_extract-1.0.0/src/cronos_extract/_format/files.py +35 -0
  32. cronos_extract-1.0.0/src/cronos_extract/_format/header.py +92 -0
  33. cronos_extract-1.0.0/src/cronos_extract/_format/record.py +192 -0
  34. cronos_extract-1.0.0/src/cronos_extract/_format/tad.py +103 -0
  35. cronos_extract-1.0.0/src/cronos_extract/cli.py +144 -0
  36. cronos_extract-1.0.0/src/cronos_extract/hexdump.py +122 -0
  37. cronos_extract-1.0.0/src/cronos_extract/koddecoder.py +456 -0
  38. cronos_extract-1.0.0/src/cronos_extract/kodump.py +87 -0
  39. cronos_extract-1.0.0/src/cronos_extract/py.typed +0 -0
  40. cronos_extract-1.0.0/src/cronos_extract/readers.py +111 -0
  41. cronos_extract-1.0.0/src/cronos_extract/survey.py +144 -0
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Organized Crime and Corruption Reporting Project
4
+ Copyright (c) 2026 Ben Hammersley
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,394 @@
1
+ Metadata-Version: 2.4
2
+ Name: cronos-extract
3
+ Version: 1.0.0
4
+ Summary: Extract data from CronosPro databases, including password-protected ones.
5
+ Keywords: cronos,cronospro,database,export,forensics
6
+ Author: Willem Hengeveld, Dirk Engling, Ben Hammersley
7
+ Author-email: Willem Hengeveld <itsme@xs4all.nl>, Dirk Engling <erdgeist@erdgeist.org>, Ben Hammersley <ben@hammersleyfutures.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.12
22
+ Project-URL: Homepage, https://github.com/hammersleyfutures/cronos-extract
23
+ Project-URL: Documentation, https://github.com/hammersleyfutures/cronos-extract/blob/main/docs/api.md
24
+ Project-URL: Changelog, https://github.com/hammersleyfutures/cronos-extract/blob/main/CHANGELOG.md
25
+ Project-URL: Issues, https://github.com/hammersleyfutures/cronos-extract/issues
26
+ Description-Content-Type: text/markdown
27
+
28
+ # cronos-extract
29
+
30
+ cronos-extract reads most databases of the [CronosPro](https://www.cronos.ru/) database software. It exports their
31
+ tables to CSV, PostgreSQL or JSON Lines. It also shows their internal structures, and it recovers the KOD of an
32
+ encrypted database.
33
+
34
+ CronosPro is popular among Russian public offices, companies and police agencies.
35
+
36
+ cronos-extract continues [cronodump](https://github.com/alephdata/cronodump) by Willem Hengeveld and Dirk Engling.
37
+ The Organized Crime and Corruption Reporting Project (OCCRP) published cronodump. cronos-extract starts from the
38
+ `master` branch of cronodump, together with the assisted KOD recovery of
39
+ [alephdata/cronodump#13](https://github.com/alephdata/cronodump/pull/13). This repository keeps the full history of
40
+ cronodump. The [changelog](https://github.com/hammersleyfutures/cronos-extract/blob/main/CHANGELOG.md) tells what is
41
+ different in cronos-extract.
42
+
43
+
44
+ ## Install
45
+
46
+ cronos-extract needs Python 3.12 or later. It has no other dependencies. The package on PyPI is `cronos-extract`.
47
+
48
+ To install the `cronos-extract` command, use one of these commands:
49
+
50
+ - With uv: `uv tool install cronos-extract`
51
+ - With pipx: `pipx install cronos-extract`
52
+ - With pip, in a virtual environment: `pip install cronos-extract`
53
+
54
+ To make sure that the command is installed, show its version:
55
+
56
+ ```bash
57
+ cronos-extract --version
58
+ ```
59
+
60
+ The command prints its name and version, for example `cronos-extract 1.0.0`.
61
+
62
+ In a clone of this repository, you can use `uv run cronos-extract` in place of `cronos-extract`. The examples that
63
+ name `test_data` use the test database of the repository. They work only in a clone.
64
+
65
+
66
+ ## Quick start
67
+
68
+ To export each table of the test database to a CSV file, use this command:
69
+
70
+ ```bash
71
+ cronos-extract export --csv test_data/all_field_types
72
+ ```
73
+
74
+ The command creates the directory `cronos-extract-YYYY-mm-dd-HH-MM-SS-ffffff/` in the current directory. This
75
+ directory holds:
76
+
77
+ - A CSV file for each table.
78
+ - `Files-<abbreviation>/` (the Files table's abbreviation, `Files-FL/` for the test database), with each file that
79
+ the database stores. This includes the files that no record refers to.
80
+ - `Files-Referenced/`, with the files that the records refer to, under their own names.
81
+
82
+ `Files-Referenced/` appears at the first record of a table with a file field. It also appears for a record whose
83
+ file field is empty.
84
+
85
+ To give the directory a different name, use `-o DIR`. The directory must not exist, because the export never
86
+ overwrites a file or a directory.
87
+
88
+ If the export stops with an error about the database definition or the KOD, the database is probably encrypted with
89
+ its own KOD. If the output is unreadable, the same is probably true. In both cases, refer to "Recover the KOD of an
90
+ encrypted database" below.
91
+
92
+
93
+ ## Export
94
+
95
+ `cronos-extract export` writes every table of a database in one of three formats: `--csv`, `--postgres` or
96
+ `--jsonl`.
97
+
98
+ ### Diagnostics
99
+
100
+ The export continues after each problem that it survives, for example a corrupt record. For each such problem, it
101
+ writes one `warning:` line on stderr at the time that the problem occurs. This line is a diagnostic. The last line
102
+ of stderr gives the number of diagnostics of each kind:
103
+
104
+ ```text
105
+ warning: corrupt_record: CroBank.dat record 88: CroBank record 88 is corrupt and is skipped: EOFError
106
+
107
+ 1 diagnostic: 1 corrupt_record
108
+ ```
109
+
110
+ The export keeps a compressed record whose CRC-32 does not agree with its data. It reports this record as
111
+ `checksum_mismatch`. If a record decompresses to more than 256 MiB, the export skips it and reports it as
112
+ `corrupt_record`.
113
+
114
+ The `.tad` file of CroBank can list deleted records. The export does not write deleted records. It writes one
115
+ `note:` line with their number on stderr, before the first table:
116
+
117
+ ```text
118
+ note: CroBank.tad lists 85 deleted records, which are not exported; inspect crodump shows what remains of them
119
+ ```
120
+
121
+ This note is not a diagnostic, and it does not change the exit status.
122
+
123
+ ### Exit status
124
+
125
+ The export exits with one of these statuses:
126
+
127
+ - 0: The export is complete, with or without diagnostics.
128
+ - 1: The export failed, for example because it cannot read the database. The last line on stderr is one `Error:`
129
+ line.
130
+ - 2: The options or arguments are not correct, or the output exists already.
131
+ - 130: The export stopped because you pressed Ctrl-C.
132
+
133
+ If the export stops after it created its output, the `Error:` line tells where the partial output is.
134
+
135
+ If the export gets `--strict` and reports a diagnostic, it exits with 1. It writes all of the output first. The test
136
+ database reports that its table definitions have an unexpected layout. Thus `--strict` exits with 1 for it:
137
+
138
+ ```bash
139
+ cronos-extract export --csv --strict -o strict test_data/all_field_types # exits 1, because of the diagnostics
140
+ ```
141
+
142
+ The header of a v4 Cro file shows when a KOD is not the KOD of the database. If the header rejects the KOD, the
143
+ export stops and exits with 1. Its `Error:` line names the `export --crack` method that can recover the correct KOD.
144
+ If the database definition cannot be decoded, the `Error:` line names `cronos-extract crack strucrack`.
145
+
146
+ ### Terminal safety
147
+
148
+ The names and values of a database can hold characters that a terminal interprets. For this reason, do not write
149
+ the PostgreSQL or JSON Lines export to a terminal. Write it to a file with `-o FILE`. The file must not exist.
150
+
151
+ The command escapes all text that it writes on stderr. Thus stderr is safe for a terminal.
152
+
153
+ ### CSV
154
+
155
+ `--csv` creates a directory with one `<table name>.csv` file for each table. The files are UTF-8 without a byte order
156
+ mark. The first row holds the field names. The first field is the system number.
157
+
158
+ `--delimiter ';'` sets a different delimiter. The default delimiter is a comma. `--no-files` does not write the two
159
+ file directories.
160
+
161
+ In a file name, the export replaces path separators with underscores. It also replaces the characters that no file
162
+ system accepts. Each file name is unique in the directory and at most 255 bytes long.
163
+
164
+ The cells hold exactly what the database holds. This includes text that a spreadsheet reads as a formula.
165
+
166
+ Open a CSV file with the CSV import of the spreadsheet, as UTF-8. Do not open it with a double-click.
167
+
168
+ ### PostgreSQL
169
+
170
+ `--postgres` writes a `CREATE TABLE` statement for each table and an `INSERT` statement for each record. Each column
171
+ has the type `TEXT`, the system number included. Thus every record loads, also a record with a value that does not
172
+ agree with its field type.
173
+
174
+ The export writes each value as it decodes it: dates as `YYYY-MM-DD`, times as `HH:MM`, and empty values as `NULL`.
175
+ If you need types, cast the columns in SQL, for example `"Entry #4"::date`.
176
+
177
+ The output starts with `SET standard_conforming_strings = on;`. Thus its string literals load correctly with each
178
+ setting of the server. PostgreSQL text cannot hold a NUL character. The export writes a NUL in a value as U+FFFD and
179
+ reports it as `replaced_nul`.
180
+
181
+ The PostgreSQL export does not include the stored files. Use `--csv` for them.
182
+
183
+ ### JSON Lines
184
+
185
+ `--jsonl` writes one JSON object on each line:
186
+
187
+ - A `table` line before the records of each table.
188
+ - A `record` line for each record.
189
+ - A `diagnostic` line for each diagnostic, at the position where it occurred.
190
+ - A `deleted_records` line before the first table, with the number of deleted records that CroBank lists.
191
+
192
+ If CroBank lists no deleted records, the `deleted_records` line is not there.
193
+
194
+ Thus a script can find the records with diagnostics. It does not have to read stderr.
195
+
196
+ ```json
197
+ {"type": "table", "table": "Люди", "table_id": 1, "abbreviation": "ЛЮ", "fields": [{"name": "Системный номер", "type": 0}, {"name": "ФИО", "type": 2}]}
198
+ {"type": "record", "table": "Люди", "table_id": 1, "record": 12, "fields": [{"name": "Системный номер", "value": "3"}, {"name": "ФИО", "value": "Иванов"}]}
199
+ {"type": "diagnostic", "kind": "invalid_value", "message": "the value is not a date; it is kept as text", "file": "CroBank.dat", "table": "Люди", "record": 13, "field": "Дата"}
200
+ ```
201
+
202
+ Each record line names its table and its fields, in the order that the table defines them. Thus you can read each
203
+ line alone. The `value` of a field is one of these:
204
+
205
+ - `null` for an empty field.
206
+ - A date as `"1985-04-02"`. A date with only its year is `"1985-00-00"`.
207
+ - A time as `"14:30"`.
208
+ - `{"name": …, "extension": …, "record": …}` for a stored file.
209
+ - The text of the field for all other fields.
210
+
211
+ The JSON Lines export does not include the stored files. Use `--csv` for them.
212
+
213
+ ```bash
214
+ cronos-extract export --jsonl -o people.jsonl test_data/all_field_types
215
+ jq -r 'select(.type == "record") | .fields[] | select(.name == "Entry #1") | .value' people.jsonl
216
+ ```
217
+
218
+ ### Large databases
219
+
220
+ The `.tad` files of a very large database can use gigabytes of memory. `--compact` reads the `.tad` files of CroStru
221
+ and CroBank from the disk, in place of memory. With `--compact`, the export is approximately 15% slower. Use
222
+ `--compact` for a very large database.
223
+
224
+
225
+ ## Survey
226
+
227
+ `cronos-extract survey` tells which CronosPro version each database uses. It reads only the 19-byte header of each
228
+ `Cro*.dat` file. It does not read records or file contents. Thus you can survey databases before you export them.
229
+
230
+ ```bash
231
+ cronos-extract survey /path/to/databases # a block of text for each database
232
+ cronos-extract survey --counts /path/to/databases # totals only, with no directory names
233
+ cronos-extract survey --jsonl /path/to/databases # one JSON object for each database, for scripts
234
+ ```
235
+
236
+ `--counts` gives the number of files of each version and generation. If the survey cannot read a header, `--counts`
237
+ also gives an `unreadable files: N` line at the end.
238
+
239
+ To survey databases in different directories as one group, do these steps:
240
+
241
+ 1. Write the paths of the directories in a text file, one path on each line.
242
+ 2. Give this file to `survey` with `--list`.
243
+
244
+ ```bash
245
+ cronos-extract survey --list /path/to/list.txt --counts
246
+ ```
247
+
248
+ The survey ignores blank lines and lines that start with `#`. A relative path starts from the current directory. If
249
+ a path is not a directory, the survey reports it on stderr and skips it. It does the same for a directory that it
250
+ cannot list. The survey reports a database under two of the paths only one time.
251
+
252
+ Versions `01.02` to `01.05` are v3. Versions `01.11`, `01.13` and `01.14` are v4. Version `01.19` is v7. The survey
253
+ reports each version that it finds, also v7 and the versions that it does not know. The `export` and `inspect`
254
+ commands read v3 and v4, but not v7.
255
+
256
+
257
+ ## Inspect
258
+
259
+ `cronos-extract inspect` shows what the export does not show, for a study of the file format. Experience with binary
260
+ dumps is helpful, because some parts of the format are not known.
261
+
262
+ The `inspect` subcommands write the names and bytes of the database to stdout without escapes. If the database is
263
+ untrusted, write their output to a file or to a pager. Do not write it to a terminal.
264
+
265
+ ```bash
266
+ cronos-extract inspect strudump -v -a test_data/all_field_types # the database and table definitions, as text
267
+ cronos-extract inspect crodump -v test_data/all_field_types # each Cro file, one byte range after the other
268
+ cronos-extract inspect recdump test_data/all_field_types # a hexdump of each CroBank record
269
+ ```
270
+
271
+ `recdump --stru`, `--index` or `--sys` dumps the records of that file in place of CroBank. `destruct` decodes a
272
+ definition that it gets as hex on stdin. `kodump` KOD-decodes a byte range of a file. Each subcommand shows its
273
+ options with `--help`.
274
+
275
+
276
+ ## Recover the KOD of an encrypted database
277
+
278
+ CronosPro can protect a database with a password. The database is then encrypted with its own KOD, in place of the
279
+ default KOD. `cronos-extract crack` recovers this KOD from the encrypted records, without the password. Its two
280
+ methods are statistical. They do not always find every entry of the KOD.
281
+
282
+ ### dbcrack
283
+
284
+ `crack dbcrack` reads the fourth byte of the CroBank and CroIndex records. This byte decodes to zero in a compressed
285
+ record. With `--silent`, dbcrack prints only the KOD. If dbcrack cannot find every entry, it exits with 1.
286
+
287
+ ```bash
288
+ KOD=$(cronos-extract crack dbcrack --silent /path/to/database)
289
+ cronos-extract export --csv --kod "$KOD" /path/to/database
290
+ ```
291
+
292
+ ### strucrack
293
+
294
+ `crack strucrack` reads CroStru, because most bytes of CroStru are zero. If strucrack cannot find every entry, it
295
+ shows the records as far as it can decode them. It suggests `-f` switches where it recognizes known text. Then it
296
+ writes the missing entries and its estimate of the KOD on stderr.
297
+
298
+ To find the missing entries, do these steps:
299
+
300
+ 1. Add the suggested `-f` switches to the command.
301
+ 2. If you can read text in a record, add `--text record:line:offset:plaintext` for this text.
302
+ 3. Run strucrack again.
303
+ 4. If strucrack does not print the KOD, do steps 1 to 3 again.
304
+
305
+ ```bash
306
+ cronos-extract crack strucrack /path/to/database
307
+ cronos-extract crack strucrack -f f103=B -f f10342 /path/to/database
308
+ ```
309
+
310
+ If strucrack gets `--noninteractive` and cannot find every entry, it exits with 1. Without `--noninteractive`, it
311
+ exits with 0 in this case.
312
+
313
+ ### The KOD options
314
+
315
+ `--kod HEX` gives the KOD as 512 hex digits. cronos-extract uses this KOD only for a file that is encrypted with its
316
+ own KOD. These files are versions `01.04`, `01.05` and all v4 versions. `--nokod` reads the records without KOD
317
+ decoding.
318
+
319
+ `export --crack dbcrack` or `export --crack strucrack` recovers the KOD and exports with it in one step. If the method
320
+ cannot recover the KOD, the export exits with 1. The `strudump`, `recdump` and `crodump` subcommands of `inspect` also
321
+ take `--crack`.
322
+
323
+
324
+ ## Python library
325
+
326
+ cronos-extract is also a Python library. The `export` command uses this library.
327
+
328
+ ```python
329
+ import cronos_extract
330
+
331
+ with cronos_extract.open("path/to/database") as bank:
332
+ for table in bank.tables:
333
+ for record in table.records():
334
+ print(record["Entry #4"].value)
335
+ print(bank.diagnostic_counts)
336
+ ```
337
+
338
+ `open()` takes `kod=` (a `cronos_extract.Kod`, or `None` for no KOD decoding), `compact=True` for very large
339
+ databases, and `on_diagnostic=`. `on_diagnostic` is a function that receives each diagnostic. The library never
340
+ prints. It skips each record that it cannot read, and it reports the record as a diagnostic.
341
+
342
+ For a database that it cannot read, `open()` raises a `cronos_extract.CronosError` or an `OSError`.
343
+ `cronos_extract.crack_kod(path, "strucrack")` or `"dbcrack"` recovers the KOD of a database that is encrypted with its
344
+ own KOD.
345
+
346
+ The [API reference](https://github.com/hammersleyfutures/cronos-extract/blob/main/docs/api.md) documents each public
347
+ name, each diagnostic kind and the promises of the API.
348
+
349
+
350
+ ## Terminology
351
+
352
+ cronos-extract uses the usual words for databases, tables, records and fields. CronosPro uses different words:
353
+
354
+ | cronos-extract | CronosPro in English | CronosPro in Russian |
355
+ |:---------------|:---------------------|:---------------------|
356
+ | database | Bank | Банк |
357
+ | table | Base | Базы |
358
+ | record | Record | Записи |
359
+ | field | Field | поля |
360
+ | system number | System Number | Системный номер |
361
+
362
+
363
+ ## Development
364
+
365
+ ```bash
366
+ uv sync # create the virtual environment with the development tools
367
+ uv run pre-commit install # lint, format, type-check and test before each commit
368
+ uv run pytest -q # run the tests
369
+ uv run ruff check && uv run ruff format --check && uv run ty check
370
+ ```
371
+
372
+ The characterisation tests in `tests/test_cli_characterisation.py` compare the output of the commands with the files
373
+ in `tests/golden/`. After a deliberate change of the output, run `uv run pytest --update-golden`. Then examine the
374
+ changes to the golden files before you commit them.
375
+
376
+ `tests/test_readme.py` runs each `cronos-extract` command in the `bash` blocks of this README. If a link in this
377
+ README is not an absolute URL, the test fails. PyPI shows this README, and a relative link does not work there.
378
+
379
+
380
+ ## License
381
+
382
+ The [MIT license](https://github.com/hammersleyfutures/cronos-extract/blob/main/LICENSE) applies to cronos-extract.
383
+ The license keeps the copyright notice of cronodump.
384
+
385
+
386
+ ## References
387
+
388
+ cronodump used the [documentation of the file format in older versions of Cronos](http://sergsv.narod.ru/cronos.htm).
389
+ It also used the [parser for the old file format](https://github.com/occrp/cronosparser) that came after this
390
+ documentation. That parser guesses offsets and obfuscation parameters with heuristics. cronodump replaced these
391
+ guesses with a stricter parser.
392
+
393
+ The [research notes](https://github.com/hammersleyfutures/cronos-extract/blob/main/docs/cronos-research.md) of this
394
+ repository document the file format.