Init repo

This commit is contained in:
hyugogirubato 2024-09-30 11:51:07 +02:00
parent 5e660e0484
commit ba48d771bb
16 changed files with 2833 additions and 6 deletions

16
.gitignore vendored
View File

@ -1,4 +1,4 @@
# ---> Python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -158,5 +158,17 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
### DVDFab ###
*.htkrules

60
CHANGELOG.md Normal file
View File

@ -0,0 +1,60 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.0] - 2024-09-29
### Added
- Added PornHub product ID for StreamFab.
- Custom ticket builder (type/method/viewer).
- Descriptions for each interception rule.
- Ability to add to an existing rules file (without overwriting user rules).
- Option to save newly created rules in a different location.
### Changed
- Removed `.htkrules` dependencies.
- Made interception rules more generic.
- Clarified debug information.
### Fixed
- Fixed detection of the default HTTP Toolkit certificate.
- Applied rules to both HTTP and HTTPS.
- Enhanced support for generic errors (files/environment, etc.).
# [1.0.2] - 2024-08-24
### Fixed
- Corrected the regex for patching MAC IDs.
- Added a missing dependency to the requirements.
# [1.0.1] - 2024-08-04
### Added
- Added a feature to bypass XML detection, enhancing compatibility with newer DVDFab versions.
- Implemented a block for usage limit requests when the user is not connected, removing restrictions on non-connected sessions.
- Removed recommendation ads, providing a cleaner and more focused user experience.
- Added a compiled executable (`.exe`) for Windows in the `dist` directory, facilitating easier deployment on Windows systems.
### Changed
- Improved the reuse of session identifiers, ensuring smoother transitions and fewer disruptions during repeated use.
### Fixed
- Fixed an error where email replacement was not functioning correctly, ensuring that all email-related operations are handled properly.
## [1.0.0] - 2024-08-01
- Initial release of the project, laying the foundation for future enhancements and features.
[1.1.0]: https://cdm-project.com/Download-Tools/DVDFabLifetimeActivation
[1.0.2]: https://cdm-project.com/Download-Tools/DVDFabLifetimeActivation
[1.0.1]: https://cdm-project.com/Download-Tools/DVDFabLifetimeActivation
[1.0.0]: https://cdm-project.com/Download-Tools/DVDFabLifetimeActivation

18
LICENSE
View File

@ -2,8 +2,20 @@ MIT License
Copyright (c) 2024 hyugogirubato
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,2 +1,96 @@
# DVDFabLifetimeActivation
# DVDFab 365 Lifetime Activation
**DVDFab 365 Lifetime Activation** is a powerful tool that automates the process of setting up activation rules and licenses for DVDFab, using the HTTP Toolkit for HTTPS interception. This project allows you to manage license tickets for DVDFab, providing seamless lifetime activation through custom rule handling. By leveraging JSON-based rule templates and certificate management, this repository helps in creating and maintaining DVDFab license activations for long-term use.
> [!IMPORTANT]
> For optimal operation (especially for the DRM API), you must have an account with access to the DRM service to bypass restrictions linked to the user/machine/IP.
## Features
- **Automated Rule Management**: This tool manages activation rules for DVDFab by loading and updating rule files in JSON format.
- **HTTPS Interception**: Utilizes HTTP Toolkit for HTTPS interception, enabling seamless integration with DVDFab.
- **Ticket Generation**: Automatically generates DVDFab lifetime activation tickets.
- **Certificate Handling**: Updates and injects the correct certificate paths for HTTPS traffic interception, using the system-specific `ca.pem` file.
- **Customizable Settings**: Users can provide custom rule paths and output locations via command-line arguments.
- **Cross-Platform Support**: Works on Windows, macOS, and Linux by identifying system-specific paths.
## How It Works
This project primarily revolves around the **HTTP Toolkit** for intercepting and managing HTTPS requests from DVDFab, and a JSON-based rules file that integrates an activation ticket. The flow includes:
1. **Loading and Updating Rules**: The tool loads default and custom rules from JSON templates and updates the rule file with a new ticket and certificate path for HTTPS traffic interception.
2. **Ticket Management**: It either uses an existing DVDFab ticket or creates a new one that grants a lifetime activation.
3. **Saving Updated Rules**: After modifying the rules and inserting the necessary activation details, the updated rules are saved into a new file that is compatible with HTTP Toolkit.
## Prerequisites
Before using the **DVDFab 365 Lifetime Activation** tool, ensure you have the following:
1. **Python 3.x** installed on your system.
2. **HTTP Toolkit** installed for HTTPS interception ([Download HTTP Toolkit](https://httptoolkit.tech/)).
3. A valid **DVDFab subscription** or ticket for activation purposes.
## Installation
1**Install the Dependencies**:
This script doesn't rely on any external Python libraries, so no additional installation is needed. However, ensure that the **HTTP Toolkit** is set up on your system.
2**Set Up Certificates**:
Make sure the **`ca.pem`** certificate from HTTP Toolkit is correctly installed on your system:
- **Windows**: Should be located in `C:\Users\<YourUsername>\AppData\Local\httptoolkit\Config\ca.pem`
- **macOS/Linux**: Should be located in `~/.config/httptoolkit/ca.pem`
## Usage
### Command-line Arguments
The script accepts several arguments to customize the rule and ticket management process:
- `--rules`: Path to the rules file (default: `template.json`).
- `--ticket`: An existing ticket string to use (optional).
- `--output`: Output path for saving the updated rules (default: script directory).
### Example Usage
1. **Generate a New Ticket** and Update Rules:
```bash
python main.py --rules custom_rules.json --output /path/to/save
```
2. **Use an Existing Ticket**:
```bash
python main.py --rules custom_rules.json --ticket "your-existing-ticket-string" --output /path/to/save
```
This will generate a DVDFab lifetime activation rule set and save it to the specified output path.
## Project Structure
- `main.py`: The primary script that handles rule management, certificate updates, and ticket processing.
- `template.json`: A default JSON rule template that provides the structure for HTTP Toolkit's rules and settings.
- `ticket.py`: A module that handles ticket generation and extraction, ensuring proper licensing is injected into the rule file.
## How the Activation Works
1. **Default Rules Template**: The `template.json` contains default rules for HTTP Toolkit's interception. It includes placeholders for the certificate and DVDFab ticket data.
2. **Certificate Path Update**: The script detects the OS and updates the certificate path in the rules to ensure proper HTTPS interception with DVDFab traffic.
3. **Ticket Generation**: A new ticket is generated with the `Ticket` class, including the ticket type (Subscriber) and expiration details (lifetime, 4096 days).
4. **DVDFab Group Update**: The `dvdfab-group` rules are updated with the newly generated ticket and inserted at the start of the rules list for priority handling.
5. **Save Rules**: The updated rules are saved in a format compatible with HTTP Toolkit to handle DVDFab traffic with lifetime activation enabled.
## Troubleshooting
- **Certificate Not Found**: Ensure that the `ca.pem` file exists in the correct directory for your operating system. If HTTP Toolkit is installed properly, this file should be automatically generated.
- **DVDFab Not Activating**: Double-check the ticket generation process and ensure the ticket type and version are correct. You can manually provide a valid ticket string using the `--ticket` argument.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Contributing
We welcome contributions! If you have suggestions or find bugs, feel free to open an issue or submit a pull request.
## Disclaimer
This tool is for educational purposes only. Unauthorized use of software or tools, including attempting to bypass licensing mechanisms, is against the terms of service of most software providers. Always ensure you have the legal right to use the software in question.

105
docs/fabcom.crt Normal file
View File

@ -0,0 +1,105 @@
-----BEGIN CERTIFICATE-----
MIIGQDCCBSigAwIBAgIQNu/9Js030ZM8qUUtvbuorTANBgkqhkiG9w0BAQsFADCB
jzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD
Ey5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB
MB4XDTI0MDYwNDAwMDAwMFoXDTI1MDYwNDIzNTk1OVowHjEcMBoGA1UEAxMTd3d3
LmR2ZGZhYnN0b3JlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKzJZZgu6CcY9ZGNVtO4u6Gv6pwWr7c2UBhR5C+saWH5exyIp73tRSWMO24db358
P+VInpidzrVV/hHdP1LEF4UEjwoha8oTFAb5e9AZIRtFAYGEHSBCEJrCvcwvROzL
Kob4KV2xx0WfGvT6NU9jwZc/cURUkBIHfsgSCeKIhQgRHKxUMHxt/o2PehSem+5/
Eu6Umsfa0NWSvwJa3w/G1O7AmhVlBm8E8riF/BrGn+Hq5SOcURPYtYbRSUbr0pTE
U68ZDmy1Lm8u5ARsX15Clgz0kCLA8fRektISzkZ5TLJ6/O5u89fx5CUcaZslnIl1
/2uzDszEgkxwdZjxnm62Zf8CAwEAAaOCAwYwggMCMB8GA1UdIwQYMBaAFI2MXsRU
rYrhd+mb+ZsF4bgBjWHhMB0GA1UdDgQWBBT1hnjQjOqDEubsslh1LxqffL2S+zAO
BgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwSQYDVR0gBEIwQDA0BgsrBgEEAbIxAQICBzAlMCMGCCsGAQUF
BwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZngQwBAgEwgYQGCCsGAQUF
BwEBBHgwdjBPBggrBgEFBQcwAoZDaHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0
aWdvUlNBRG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAjBggrBgEF
BQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wLwYDVR0RBCgwJoITd3d3LmR2
ZGZhYnN0b3JlLmNvbYIPZHZkZmFic3RvcmUuY29tMIIBfgYKKwYBBAHWeQIEAgSC
AW4EggFqAWgAdgDPEVbu1S58r/OHW9lpLpvpGnFnSrAX7KwB0lt3zsw7CAAAAY/h
K5aMAAAEAwBHMEUCIHufENt/oKFvU/xUcXGRI59szxl9CnaTjU8AMn4BqTVWAiEA
206fHfzMKiQM1GWf91zHmEHYQdg+HjH7UocW9ickjAwAdwCi4wrkRe+9rZt+OO1H
Z3dT14JbhJTXK14bLMS5UKRH5wAAAY/hK5YlAAAEAwBIMEYCIQDQ9FDaJs1kdvGr
xAqfOllxRHxW5DKXkTyWbWtm5KYIlQIhAOub2+LCC5pbrcYGR0tpbpG7pwM4YgNW
+eRtXedz4yjiAHUATnWjJ1yaEMM4W2zU3z9S6x3w4I4bjWnAsfpksWKaOd8AAAGP
4SuWJQAABAMARjBEAiArjJhR95vIOkD2KNAX5Utkq0YOe9tjd+sXHPGHO8BMzwIg
SbO2ZStYSgqgCSD5V0ORGVZ601XZS/et1EO6B+jpgmYwDQYJKoZIhvcNAQELBQAD
ggEBADnCMwncbylDk/pd5PjX1Zibnp+sbrhekeXMXPFcXcaf9Q2nAMrvpTAmYZBL
F9ixRJk0rfxQnPa6BKfspTxP6xAo/FttSS6S7x6kYFwea0jozf8PwsJjpWZ2adlO
mEIGXZHtxuWgiRzkmogc6rWAfBVYGNngf2u2B6fiVsY6GcU6EqgUXra5qm9FUWSq
ERVodnIjAUDA/hVIZT3YAjfXVBMWWKCgRzt7OXtu/lN8akuhcUqzkZcQA6qhHPFv
w6UyWJKKUS1BPKNbqxRO/qZdhUALgQIHnt4s846THt3sjb499CjFPuRRnvE9jpkw
mk81gk2B8VzG4SpHz1eA8P7rCZM=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV
BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N
TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj
eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E
oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk
Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY
uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j
BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb
+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw
CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0
LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr
BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv
bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov
L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H
ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH
7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi
H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx
RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv
xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

104
docs/fabnew.crt Normal file
View File

@ -0,0 +1,104 @@
-----BEGIN CERTIFICATE-----
MIIGKzCCBROgAwIBAgIRANiQS4APiiZWfW0/nFyT9GowDQYJKoZIhvcNAQELBQAw
gY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UE
AxMuU2VjdGlnbyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD
QTAeFw0yMzA4MjEwMDAwMDBaFw0yNDA5MjAyMzU5NTlaMBYxFDASBgNVBAMMCyou
ZHZkZmFiLmNuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsEKmgKeE
dU2VI1AX4q/fVQjUGGzUuPJL3rwWoojWGMqrzN9aYGRRPDY3OTB3jlV9DO9kx1ur
OHT2UCFf5xFrq+/Yg2jtjFwatk5UwgNj+VWcsRIW41MELpCcpDVqWH6VY1zWSIuj
lSnPKfuW8UVdtjyfAHg2KtjPgcJnckLxl2jrnfKEc1UJcJ4R9J4midZGLvjEyZmI
B8l2uvNSV+AmJTNIqY0blPdp0caHvLjoQB9hvPT4TJkdami86CsXJIvsQVeYOS7Q
QS+M2HptLaP54huehfq5i7V29t08MnqDofhA7VM7rPSeyuBm7VrAvjHVRaZOHE6G
5cbZvUwfSVvAIQIDAQABo4IC+DCCAvQwHwYDVR0jBBgwFoAUjYxexFStiuF36Zv5
mwXhuAGNYeEwHQYDVR0OBBYEFFy4rbNIO4JlwuVhG1dKC5xIrBIgMA4GA1UdDwEB
/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
BQcDAjBJBgNVHSAEQjBAMDQGCysGAQQBsjEBAgIHMCUwIwYIKwYBBQUHAgEWF2h0
dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAECATCBhAYIKwYBBQUHAQEEeDB2
ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29SU0FE
b21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCMGCCsGAQUFBzABhhdo
dHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAhBgNVHREEGjAYggsqLmR2ZGZhYi5jboIJ
ZHZkZmFiLmNuMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgB2/4g/Crb7lVHC
Ycz1h7o0tKTNuyncaEIKn+ZnTFo6dAAAAYoWS5ILAAAEAwBHMEUCIBAj38PxL1zL
My/YDxD8JWYKqdNtanZbJ9hC1jB+QdDhAiEA1y3WRbjxxBlDmaR1JLUtVWzWBXJv
3K5Kx1w+Sh8zKscAdgDatr9rP7W2Ip+bwrtca+hwkXFsu1GEhTS9pD0wSNf7qwAA
AYoWS5JdAAAEAwBHMEUCIQDLCAW/LqGRDvHkd45SzTpL7Zjisxq/QlJ9Coiugrlu
PQIgCfW5NZuIFBmxvEa11l+MI35j32osRn/X6219rW9+2tkAdgDuzdBk1dsazsVc
t520zROiModGfLzs3sNRSFlGcR+1mwAAAYoWS5KEAAAEAwBHMEUCIFwdgul9xtED
xem3h+T9V4Vhiom6RUQndOtpGIF1DCc0AiEAoVNMe3txvwm+lTy5vLth0v4cYttG
EKtGJvJzMPvPBl8wDQYJKoZIhvcNAQELBQADggEBABWnaaXEti68U9pUFuvZu2Dp
N4l/Fef1cKKsPO+JwzdUI4G0vt/ROSGjxFGmHyvCFiXIe7FFW1KO+5VsPMdrJODj
aAq4yOa1389QSSvtH55g9fmQuzN4p/KBkzs55A4cOvjL80RGfPVw4UkOZFcmcmHp
6+rySKxV0lp4ddGoz98JQ/fiFJiWvw6wNCfnKbCf575jDefa56JosWMnOOGMv78y
CJJnu2CWjceA7m536OuD5cK/hoptAAsuzq4lmFnFct32Z+GrEgkvLVLwae4t9YNF
KQYwTmVohU1/NVL0Icn5AqQugeFs61k5P4ORjlUTG3MhJZhUmsOWKgoO2hbH5UY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV
BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N
TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj
eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E
oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk
Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY
uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j
BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb
+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw
CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0
LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr
BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv
bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov
L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H
ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH
7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi
H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx
RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv
xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

BIN
docs/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
docs/images/import_ca.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
docs/images/mock_rules.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

165
docs/server.py Normal file
View File

@ -0,0 +1,165 @@
import re
import secrets
import string
import requests
import xmltodict
from flask import Flask, request, Response
from requests_toolbelt import MultipartEncoder
# utils.py
def parse_boundary(data: str) -> dict:
"""
Parse the multipart form data to extract fields.
Parameters:
- data (str): The raw multipart form data as a string.
Returns:
- dict: A dictionary with form field names as keys and field values as values.
"""
fields = {}
# Split parts using the boundary (separator) and process each part
items = data.split('-' * 26)[1:]
for item in items:
lines = item.splitlines()
if len(lines) < 3:
continue
# Extract the field name from the Content-Disposition header
key = re.match(r'^Content-Disposition:\s*form-data;\s*name="([^"]+)"\s*$', lines[1]).group(1)
# Join the remaining lines as the field value
fields[key] = '\n'.join(lines[3:])
return fields
# session.py
class Session:
DOMAINS = ['hotmail.com', 'gmail.com', 'yahoo.com', 'outlook.com', 'protonmail.com', 'yandex.com']
CHARACTERS = string.ascii_lowercase + string.digits + '.-'
def __init__(self):
self.email = None
self.machine_id = None
self.usage = 0
self.__update()
def __update(self) -> None:
"""
Update the session with a new random email and machine ID.
"""
length = secrets.choice(range(5, 15))
self.email = '{local}@{domain}'.format(
local=''.join(secrets.choice(self.CHARACTERS) for _ in range(length)),
domain=secrets.choice(Session.DOMAINS)
)
self.machine_id = ':'.join([
'-'.join(f'{b:02x}' for b in secrets.token_bytes(6)),
'-'.join(f'{b:02x}' for b in secrets.token_bytes(6))
])
print(f'Email: {self.email}')
print(f'Machine ID: {self.machine_id}')
def __repr__(self) -> str:
return '{name}({items})'.format(
name=self.__class__.__name__,
items=', '.join([f'{k}={repr(v)}' for k, v in self.__dict__.items()])
)
def patch_boundary(self, data: dict) -> dict:
"""
Modify specific fields in the data dictionary based on predefined rules.
Parameters:
- data (dict): A dictionary containing form field names and values.
Returns:
- dict: The modified dictionary with updated field values.
"""
if self.usage == 3:
self.usage = 0
self.__update()
# Update fields based on rules
for key, value in data.items():
if key in ('TK', 'PW', 'D', 'F') and re.match(r'[0-9a-f]{32}', value):
data[key] = value # '' # Replace token
if re.match(r'^([0-9a-f]{2}-){5}[0-9a-f]{2}(:([0-9a-f]{2}-){5}[0-9a-f]{2})?', value):
data[key] = self.machine_id # Replace MAC
elif re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', value):
data[key] = self.email # Replace email
elif re.match(r'^365$', value):
data[key] = 'trial' # Replace subscription
self.usage += 1
return data
# app.py
session = Session()
app = Flask(__name__)
app.config.update(
DEBUG=True,
SECRET_KEY=secrets.token_hex(16),
ALLOWED_HOSTS=['127.0.0.1', 'localhost']
)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>', methods=['POST'])
def catch_all(path: str) -> Response:
global session
"""
Handle all POST requests, modify the data, and forward it to the same URL over HTTPS.
Parameters:
- path (str): The path part of the URL (used for routing).
Returns:
- Response: A Flask Response object with the status and content of the forwarded request.
"""
is_auth = re.match(r'^auth/trial_disc\.php', path)
headers = dict(request.headers)
del headers['Connection']
del headers['Content-Length']
body = request.get_data()
if not is_auth:
boundary = parse_boundary(body.decode('utf-8'))
patched = session.patch_boundary(boundary)
mp_encoder = MultipartEncoder(patched)
headers['Content-Type'] = mp_encoder.content_type
body = mp_encoder.read()
# Forward the request to the same URL over HTTPS
r = requests.request(
method=request.method,
url=request.url.replace('http://', 'https://'),
params=request.args.to_dict(),
data=body,
headers=headers,
cookies=request.cookies
)
content = r.content
if is_auth:
data = xmltodict.parse(content)
if data.get('TrialOpenDiscInfo', {}).get('@MacID'):
data['TrialOpenDiscInfo']['@MacID'] = session.machine_id
if data.get('TrialOpenDiscInfo', {}).get('DiscInfo'):
del data['TrialOpenDiscInfo']['DiscInfo']
content = xmltodict.unparse(data, pretty=False, full_document=False)
# Return the response from the forwarded request
return Response(status=r.status_code, response=content)
# Run the Flask app
app.add_url_rule('/', 'catch_all', catch_all, defaults={'path': ''})
app.add_url_rule('/<path:path>', 'catch_all', catch_all, defaults={'path': ''})
app.run(host='127.0.0.1', port=5000)

105
docs/streamfab-us-chain.crt Normal file
View File

@ -0,0 +1,105 @@
-----BEGIN CERTIFICATE-----
MIIGNTCCBR2gAwIBAgIQW19xjDWCt1zPCZOS4YJiQjANBgkqhkiG9w0BAQsFADCB
jzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD
Ey5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB
MB4XDTI0MDEwOTAwMDAwMFoXDTI1MDEwOTIzNTk1OVowGjEYMBYGA1UEAwwPKi5z
dHJlYW1mYWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwBxA
5k/xyHsc2idH/N9y+B8zgMNRacIqOxm0zb2MjmYXNM5pZk3NKJoucLLgMMqar8s2
E6LYwHrBsDWcmi8UopP5U1pqTWSjSK98GQyaaosLiI0eoSagvBH4mWTkm+qDV3vO
rGz5NnXXmoVqXMGVk15/4JaKzv1ksZjOCtN5L7wG8fbuzxNUK8f51+avfDCvB5ZY
aBTv5J7NeNMvXxoUe0LGwgEBYeCYxSQCyGzi+2Cmc+j9Qwk9xP/stV6dspJnvEDP
HF7CmICib6EYAOYoeovAm4k7SdWhbOIOT3GesREjDsJJgin0xCFpOSI/5fWXf4Ff
H7g1WvPFbtWssGuAGQIDAQABo4IC/zCCAvswHwYDVR0jBBgwFoAUjYxexFStiuF3
6Zv5mwXhuAGNYeEwHQYDVR0OBBYEFP5TmhConW+mZO6BeKmyF0SHUpzMMA4GA1Ud
DwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
BgEFBQcDAjBJBgNVHSAEQjBAMDQGCysGAQQBsjEBAgIHMCUwIwYIKwYBBQUHAgEW
F2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAECATCBhAYIKwYBBQUHAQEE
eDB2ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29S
U0FEb21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCMGCCsGAQUFBzAB
hhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTApBgNVHREEIjAggg8qLnN0cmVhbWZh
Yi5jb22CDXN0cmVhbWZhYi5jb20wggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB2
AM8RVu7VLnyv84db2Wkum+kacWdKsBfsrAHSW3fOzDsIAAABjOxWztcAAAQDAEcw
RQIgUtkTDufYxtIZe2KaY6ISOMITcbTcnIASZi6zYHKHSIkCIQC1eOhczsh0zeTD
mi+wXfugyDfyG7aGc5fJ3yvdntywugB2AKLjCuRF772tm3447Udnd1PXgluElNcr
XhssxLlQpEfnAAABjOxWztAAAAQDAEcwRQIhAJJM7mrVaxS10+AK9vYIykKBCMrq
hoCoaHSxxk8Z00+BAiALNTj+oLrsM0jIQEQwU+cFzh8t7vMK3ZazDDc+gAI9IAB1
AE51oydcmhDDOFts1N8/Uusd8OCOG41pwLH6ZLFimjnfAAABjOxWzo8AAAQDAEYw
RAIgZ4silEWDt7/mqpiWhUDUu35k3ydsYAFZ6yEpLG4l/aMCIGbAsXAfho3iumKS
zZTOCCzm+C/6Lw9zKvWbPl7xCNlCMA0GCSqGSIb3DQEBCwUAA4IBAQBWXnnWtoKn
fngd9m2d5FWLMyDg2V2ph5aPW7mfodAwrEHLvqBBxS2jZ8VTtPsEybf8/Va4H52J
3Bxx3c/8sfiD7xXC2O2+H8RCv/uKFene3g6uKl0e+odr6v+XrW51fXKqDn1ApfdG
faQXdlDbs/vs252IySTLqLiIzvbbZQyVgrMf4Ju7Ao4dzEer02PdLGhS4p8iAEg2
sJ4H57yUUeZQ6H4VOvqpGlVzhyjWTZDM31tMVWYdXqYXEI+iDpz7+9GdAEPRHn8Q
Sct3iAOzTWhGupyAOn3Y2OKQtWI+05QE+6eUMKF13CqzprgFa/sxiMRMolOTqb9r
E0EAYeHAaPdq
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx
MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV
BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE
ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N
TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj
eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E
oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk
Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY
uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j
BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb
+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw
CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0
LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr
BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv
bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov
L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H
ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH
7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi
H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx
RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv
xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

5
src/requirements.txt Normal file
View File

@ -0,0 +1,5 @@
pathlib~=1.0.1
requests~=2.31.0
xmltodict~=0.13.0
Flask~=3.0.3
requests-toolbelt~=1.0.0

136
src/rules.py Normal file
View File

@ -0,0 +1,136 @@
import argparse
import json
import os
from pathlib import Path
# Importing Ticket and TicketType from the ticket module
from ticket import Ticket, TicketType
# Define paths and defaults
PARENT = Path(__file__).parent
DEFAULT_RULES_PATH = Path('template.json')
def get_ca_path() -> Path:
"""
Determine the path to the 'ca.pem' certificate file based on the operating system.
The certificate is used by HTTPToolkit for HTTPS interception.
Returns:
Path: The full path to the 'ca.pem' file.
"""
if os.name == 'nt': # Windows
return Path.home() / 'AppData' / 'Local' / 'httptoolkit' / 'Config' / 'ca.pem'
else: # Linux/macOS (Unix-like)
return Path.home() / '.config' / 'httptoolkit' / 'ca.pem'
def load_rules(rules_path: Path) -> dict:
"""
Load rules from a specified file path in JSON format.
Args:
rules_path (Path): Path to the rules file.
Returns:
dict: Loaded JSON rules as a Python dictionary.
"""
return json.loads(rules_path.read_bytes())
def update_certificate_path(rules: dict) -> None:
"""
Update the 'default-certificate' file path in the rules to point to the system's 'ca.pem' certificate.
Args:
rules (dict): The current rules JSON data to modify.
"""
try:
# Locate the 'default-group' and its 'default-certificate' item
group = next(item for item in rules['items'] if item['id'] == 'default-group')
certificate = next((item for item in group['items'] if item['id'] == 'default-certificate'), None)
if certificate:
ca_path = get_ca_path() # Get the path to the 'ca.pem' file
if ca_path.is_file():
certificate['handler']['filePath'] = str(ca_path)
print('[+] Updated default certificate path')
else:
print('[!] CA certificate not found')
except StopIteration:
print('[!] Could not find default certificate group or item')
def save_rules(rules: dict, output_path: Path) -> None:
"""
Save the updated rules as a JSON file.
Args:
rules (dict): The modified rules to save.
output_path (Path): The path to save the rules file.
"""
# Ensure output directory exists
output_path.mkdir(parents=True, exist_ok=True)
# Define the file name
file_path = output_path / 'HTTPToolkit_DVDFab.htkrules'
# Write the rules to the file in JSON format
file_path.write_text(json.dumps(rules, separators=(',', ': ')))
print(f'[+] Rules saved to {file_path}')
if __name__ == '__main__':
# Set up argument parsing
parser = argparse.ArgumentParser(description='DVDFab-Rules: Manage rules and tickets for HTTPToolkit and DVDFab.')
# Command-line arguments
parser.add_argument('--rules', type=Path, metavar='<path>', default=DEFAULT_RULES_PATH, help='Path to rules file (default: template.json)')
parser.add_argument('--ticket', type=str, metavar='<ticket>', help='Existing ticket string (optional)')
parser.add_argument('--output', type=Path, metavar='<path>', default=PARENT, help='Output path for saving the updated rules')
# Parse the command-line arguments
args = parser.parse_args()
try:
# Load default and specified rules
default_rules = load_rules(DEFAULT_RULES_PATH)
rules = load_rules(args.rules)
# Update certificate path if the rules match the default template
if rules == default_rules:
update_certificate_path(rules)
# Remove any existing 'dvdfab-group' from the rules
rules['items'] = [item for item in rules['items'] if item['id'] != 'dvdfab-group']
# Handle ticket creation or extraction
ticket = args.ticket or Ticket.create({
'type': TicketType.SUBSCRIBER.name,
'version': 6200,
'expire': 4096 # Set ticket expiration days
}).export()
# Extract ticket details for display
ticket_info = Ticket.extract(ticket)
for key, value in ticket_info.items():
print(f'[*] {key.capitalize()}: {value}')
# Add DVDFab group and ticket data to rules
dvdfab_group = next(item for item in default_rules['items'] if item['id'] == 'dvdfab-group')
auth_item = next(item for item in dvdfab_group['items'] if item['id'] == '6955a688-8bc2-4f63-92cd-141f9debb97a')
# Insert the ticket data into the auth handler
auth_item['handler']['data']['data'] = list(ticket.encode('utf-8'))
# Add the updated DVDFab group to the start of the rules
rules['items'].insert(0, dvdfab_group)
print('[+] Added DVDFab rules')
# Save the updated rules to the output path
save_rules(rules, args.output)
except Exception as e:
# Print any errors that occur during execution
print(f'[!] {e}')
exit(1)

1641
src/template.json Normal file

File diff suppressed because it is too large Load Diff

388
src/ticket.py Normal file
View File

@ -0,0 +1,388 @@
import argparse
import hashlib
import json
import re
import secrets
from datetime import datetime, timedelta
from enum import Enum
import requests
from requests_toolbelt import MultipartEncoder
# Constants for default empty values
EMPTY_SERIAL = '00000000000000000000000000000000'
EMPTY_MAC = '00-00-00-00-00-00:00-00-00-00-00-00'
# Enum to represent ticket types
class TicketType(Enum):
SUBSCRIBER = 3
LIFETIME = 2
FREE = 1
NO_LOGIN = 0
# Enum to represent client types (operating systems)
class ClientType(Enum):
WINDOWS = 94
ANDROID = 51
MAC = 0 # TODO: Define the Mac Client ID
class Ticket:
MAGIC = 1
# List of valid product IDs (these could represent different software or features)
PRODUCTS = [
# StreamFab
308, 310, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 337, 339, 340, 342, 346, 348,
349, 350, 352, 353, 356, 357, 358, 359, 360, 361, 362, 364, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375,
376, 377, 378, 379, 380, 381, 500,
# Others
2, 11, 20, 21, 22, 50, 55, 60, 61, 62, 63, 70, 91, 92, 93, 94, 95, 96, 97, 98, 200, 201, 208, 209, 213, 214,
215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 300, 301, 302, 303, 304,
305, 306, 307, 309, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 336, 338, 341, 347, 351, 354, 355, 363,
365, 384, 394, 396, 397, 398, 400, 401, 402, 403, 404, 405, 407, 409, 410, 412, 414, 1002, 1011, 1020, 1021,
1022, 1050, 1055, 1060, 1061, 1062, 1070, 1095, 1096, 1097, 1098, 1200, 1201, 1208, 1209, 1213, 1214, 1215,
1216, 1217, 1218, 1219, 1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1300, 1301, 1302,
1303, 1304, 1305, 1306, 1307, 1308, 1310, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1322, 1323,
1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341,
1342, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362,
1363, 1364, 1365, 1366, 1367, 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1376, 1377, 1378]
def __init__(self, token: str, data: dict):
"""
Initialize the ticket with a user token and relevant ticket data.
Args:
token (str): User's unique token.
data (dict): Dictionary containing ticket information (type, version, expiration).
"""
self.token = token
self.type = TicketType[data['type']]
self.version = data['version']
self.expire = 75616 if self.type == TicketType.LIFETIME else data['expire']
def __repr__(self) -> str:
"""Provide string representation for debugging."""
return '{name}({items})'.format(
name=self.__class__.__name__,
items=', '.join([f'{k}={repr(v)}' for k, v in self.__dict__.items()])
)
def export(self) -> str:
"""
Export the ticket information into a serialized format.
This method constructs a string representing the ticket data and expiration date.
Returns:
str: A serialized string representation of the ticket, including magic number,
product details, and configuration.
"""
current = datetime.now() # Current date and time
expire = current + timedelta(days=self.expire) # Expiration date
# Determine products that this ticket covers
products = self.PRODUCTS if self.type in (TicketType.LIFETIME, TicketType.SUBSCRIBER) else []
# Ticket configuration based on ticket type
config = {
'VP': -1, # Validity period (-1 for lifetime/subscription)
'VPT': 0, # Validity period time (specific expiration timestamp for non-lifetime tickets)
'OV': self.version, # Software version
'BV': '', # Base version (reserved for future use)
'AD': 1, # Additional field (1 for Subscriber and Lifetime)
'SUB': '', # Subscription identifier (if available)
'UT': 0, # Usage Type
'UL': 1, # User Login required flag
'ML': (0, 6, 1), # Machine limits or other codes (used machines, max allowed, currently active)
'S': self.token, # User's token
'TI': round(current.timestamp()), # Ticket issued timestamp
'TM': 0 # Reserved field
}
# Adjust config for different ticket types
if self.type == TicketType.NO_LOGIN:
# For NO_LOGIN type tickets, certain fields are removed
del config['AD']
del config['S']
else:
del config['UL'] # No need for user login in other types
config['ML'] = (1, 6, 2) # Adjust machine limits
if self.type == TicketType.SUBSCRIBER:
config['VP'] = self.expire # Set validity period for Subscriber type
config['VPT'] = round(expire.timestamp()) # Set the expiration timestamp
if self.type == TicketType.FREE:
config['ML'] = (1, 1, 1) # Restrict machine limits for Free ticket type
# Convert machine limits to string format
config['ML'] = '-'.join(map(str, config['ML']))
# Construct the ticket export string with MAGIC number and product details
items = [self.MAGIC]
# Add products with expiration timestamps
items += [f'{p}:{round(expire.timestamp())}' for p in products]
# Add configuration key-value pairs
items += [f'{k}:{v}' for k, v in config.items()]
# Return the serialized string
return '|'.join(map(str, items))
@classmethod
def from_token(cls, data: dict):
"""
Create a ticket instance using an existing user token.
This method validates the token length and initializes a new Ticket instance
using the provided token and data.
Args:
data (dict): Dictionary containing token and ticket data.
Returns:
Ticket: An instance of Ticket created using the provided token.
"""
token = data['token']
# Token must be 32 characters long
assert len(token) == 32, 'Invalid Token Length'
# Return a new Ticket instance
return cls(token, data)
@classmethod
def from_account(cls, data: dict):
"""
Create a ticket instance using account credentials.
This method sends a login request to the server and retrieves the token and ticket details.
Args:
data (dict): Dictionary containing account login credentials (email and password).
Returns:
Ticket: An instance of Ticket initialized with the retrieved token and ticket data.
"""
mp_encoder = MultipartEncoder({
'DI': json.dumps({
'MAC': data['mac'],
'DS': data['DS'],
'IS': data['IS'],
'BI': data['BI']
}, separators=(',', ':')),
'H': data['mac'],
'V': str(data['version']),
'C': str(ClientType[data['client']].value),
'S': '',
'U': data['email'],
'P': hashlib.md5(data['password'].encode('utf-8')).hexdigest(),
'T': '0'
})
r = requests.request(
method='POST',
url='https://www.dvdfabstore.com/auth/v5/',
data=mp_encoder,
headers={
'Accept': '*/*',
'Content-Type': mp_encoder.content_type,
'User-Agent': 'FabApp/3.0'
}
)
r.raise_for_status()
# Extract token from response
m = re.search(r'S:([a-f0-9]+)', r.text)
assert m, 'Invalid Login Credentials'
# Return a new Ticket instance
return cls(m.group(1), data)
@staticmethod
def extract(value: str) -> dict:
"""Extract ticket details and return relevant information.
Args:
value (str): The ticket string to extract details from.
Returns:
dict: A dictionary containing the type of ticket, expiration time,
number of products, and configuration details.
"""
items = value.split('|')
current = round(datetime.now().timestamp())
assert int(items[0]) == Ticket.MAGIC, 'Invalid Magic Ticket'
config = {}
expires = []
products = []
for item in items[1:]:
key, value = item.split(':')
# Attempt to convert the value to an integer
try:
value = int(value)
except ValueError:
pass
if key.isdigit():
# Save expiration timestamp
expires.append(value)
# Verify product ID
key = int(key)
if key not in Ticket.PRODUCTS:
print(f'[!] Unknown product: {key}')
# Add the valid product ID to the list
products.append(key)
# Check if the product has expired
if value < current:
print(f'[!] Expired product: {key}')
else:
if key == 'ML':
# Extract used device
value = tuple(map(int, value.split('-')))
# Add non-product key-value pairs to config
config[key] = value
# Determine the type of ticket based on the extracted configuration
if 'UL' in config:
_type = TicketType.NO_LOGIN
elif config['VP'] != -1:
_type = TicketType.SUBSCRIBER
elif config['ML'] == (1, 1, 1):
_type = TicketType.FREE
else:
_type = TicketType.LIFETIME
# Return a dictionary containing the type, expiration, number of products, and configuration
return {
'type': _type.name,
'expire': timedelta(seconds=max(expires) - current).days if expires else 0,
'token': config.get('S'),
'products': len(products),
'devices': config['ML'],
'version': config['OV']
}
@classmethod
def from_ticket(cls, data: dict):
"""Create a ticket from a previous ticket.
Args:
data (dict): Dictionary containing ticket data.
Returns:
Ticket: An instance of Ticket created from the given data.
"""
# Extract details from the ticket
extracted = cls.extract(data['ticket'])
# Retrieve user token from extracted data
token = extracted['token']
assert token, 'Missing User Token'
# Update the software version in the provided data
data['version'] = extracted['version']
# Return a new Ticket instance
return cls(token, data)
@classmethod
def create(cls, data: dict):
"""Generate a new ticket with default values.
Args:
data (dict): Dictionary containing ticket data.
Returns:
Ticket: An instance of Ticket with a newly generated token.
"""
# Generate a secure random token
token = secrets.token_hex(16)
# Return a new Ticket instance with the generated token
return cls(token, data)
if __name__ == '__main__':
"""Main function to parse arguments and handle ticket creation and extraction."""
parser = argparse.ArgumentParser(description='DVDFab-Ticket: Manage user tickets for DVDFab software.')
# Subparsers for account actions
subparsers = parser.add_subparsers(dest='action', help='Ticket management actions')
# Subparser for login
login_parser = subparsers.add_parser('login', help='Use account credentials')
login_parser.add_argument('email', type=str, metavar='email', help='Email address')
login_parser.add_argument('password', type=str, metavar='password', help='Password')
login_parser.add_argument('--client', required=False, type=str, choices=[t.name for t in list(ClientType)], default=ClientType.WINDOWS.name, help='Client type')
login_parser.add_argument('--mac', required=False, type=str, metavar='<mac>', default=EMPTY_MAC, help='MAC address')
login_parser.add_argument('--DS', required=False, type=str, metavar='<DS>', default=EMPTY_SERIAL, help='Device serial')
login_parser.add_argument('--IS', required=False, type=str, metavar='<IS>', default=EMPTY_SERIAL, help='Device ID')
login_parser.add_argument('--BI', required=False, type=str, metavar='<BI>', default=EMPTY_SERIAL, help='BIOS information')
# Subparser for token
# Allows user to log in using an existing token rather than credentials
token_parser = subparsers.add_parser('token', help='Use an existing user token')
token_parser.add_argument('token', type=str, metavar='token', help='User token')
# Subparser for ticket
# Allows re-use of a previous ticket
ticket_parser = subparsers.add_parser('ticket', help='Use an existing ticket')
ticket_parser.add_argument('ticket', type=str, metavar='ticket', help='Old user ticket')
# Subparser for extracting info
# Allows users to extract and view information from an existing ticket string
info_parser = subparsers.add_parser('info', help='Extract info from ticket')
info_parser.add_argument('ticket', type=str, metavar='ticket', help='Ticket string')
# Arguments for creating a new ticket
# Allow users to customize the ticket type, version, and expiration
parser.add_argument('--type', required=False, type=str, choices=[t.name for t in list(TicketType)], default=TicketType.SUBSCRIBER.name, help='Ticket type')
parser.add_argument('--version', required=False, type=int, metavar='<version>', default=6200, help='Software version')
parser.add_argument('--expire', required=False, type=int, metavar='<expire>', default=365, help='Days until ticket expires (default: 365)')
# Parse the command-line arguments
args = parser.parse_args()
try:
# Handle ticket information extraction
if args.action == 'info':
infos = Ticket.extract(args.ticket)
for key, value in infos.items():
print(f'[*] {str(key).capitalize()}: {value}')
else:
# Handle ticket creation or loading based on token, login, or ticket
if args.action == 'token':
# Generate ticket from an existing token
ticket = Ticket.from_token(vars(args))
elif args.action == 'login':
# Generate ticket after logging in
ticket = Ticket.from_account(vars(args))
elif args.action == 'ticket':
# Generate ticket from a previous ticket
ticket = Ticket.from_ticket(vars(args))
else:
# Create a new ticket with default settings
ticket = Ticket.create(vars(args))
# Output the serialized ticket string
print(ticket.export())
except Exception as e:
# Print exception message if an error occurs during ticket creation or processing
print(f'[!] {e}')
exit(1)