diff options
| -rw-r--r-- | device_ticket.bat | 166 | ||||
| -rw-r--r-- | kh_editlic.py | 70 | ||||
| -rw-r--r-- | resignlic.py | 125 | ||||
| -rw-r--r-- | splicenseblock.hexpat | 142 |
4 files changed, 503 insertions, 0 deletions
diff --git a/device_ticket.bat b/device_ticket.bat new file mode 100644 index 0000000..46ec8e6 --- /dev/null +++ b/device_ticket.bat @@ -0,0 +1,166 @@ +<# :
+@REM BSD 3-Clause License
+@REM
+@REM Copyright(c) 2023, echnobas
+@REM All rights reserved.
+@REM
+@REM Redistribution and use in source and binary forms, with or without
+@REM modification, are permitted provided that the following conditions are met:
+@REM
+@REM 1. Redistributions of source code must retain the above copynotice, this
+@REM list of conditions and the following disclaimer.
+@REM
+@REM 2. Redistributions in binary form must reproduce the above copynotice,
+@REM this list of conditions and the following disclaimer in the documentation
+@REM and/or other materials provided with the distribution.
+@REM
+@REM 3. Neither the name of the copyholder nor the names of its
+@REM contributors may be used to endorse or promote products derived from
+@REM this software without specific prior written permission.
+@REM
+@REM THIS SOFTWARE IS PROVIDED BY THE COPYHOLDERS AND CONTRIBUTORS "AS IS"
+@REM AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+@REM IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+@REM DISCLAIMED. IN NO EVENT SHALL THE COPYHOLDER OR CONTRIBUTORS BE LIABLE
+@REM FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+@REM DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+@REM SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+@REM CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+@REM OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+@REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+@echo off &chcp 850 >nul &pushd "%~dp0"
+fltmc >nul 2>&1 || (
+ powershell Start-Process -FilePath "%~f0" -ArgumentList "%cd%" -verb runas >NUL 2>&1
+ exit /b
+)
+set "psScript=%~f0"
+powershell -nop -c "& ([ScriptBlock]::Create((Get-Content """$env:psScript""" -Raw)))" & exit /b
+: #>
+###################################### SUBLICENSE BEGIN ######################################
+# BSD 3-Clause License
+#
+# Copyright(c) 2019, Tobias Heilig
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copynotice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copynotice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyholder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYHOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYHOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+try {
+ & {
+ $ErrorActionPreference = 'Stop'
+ [void] [impsys.win32]
+ }
+}
+catch {
+ Add-Type -TypeDefinition @"
+ using System;
+ using System.Runtime.InteropServices;
+ namespace impsys {
+ public class win32 {
+
+ [DllImport("kernel32.dll", SetLastError=true)]
+ public static extern bool CloseHandle(
+ IntPtr hHandle);
+
+ [DllImport("kernel32.dll", SetLastError=true)]
+ public static extern IntPtr OpenProcess(
+ uint processAccess,
+ bool bInheritHandle,
+ int processId);
+
+ [DllImport("advapi32.dll", SetLastError=true)]
+ public static extern bool OpenProcessToken(
+ IntPtr ProcessHandle,
+ uint DesiredAccess,
+ out IntPtr TokenHandle);
+
+ [DllImport("advapi32.dll", SetLastError=true)]
+ public static extern bool DuplicateTokenEx(
+ IntPtr hExistingToken,
+ uint dwDesiredAccess,
+ IntPtr lpTokenAttributes,
+ uint ImpersonationLevel,
+ uint TokenType,
+ out IntPtr phNewToken);
+
+ [DllImport("advapi32.dll", SetLastError=true)]
+ public static extern bool ImpersonateLoggedOnUser(
+ IntPtr hToken);
+
+ [DllImport("advapi32.dll", SetLastError=true)]
+ public static extern bool RevertToSelf();
+ }
+ }
+"@
+}
+
+$winlogonPid = Get-Process -Name "winlogon" | Select-Object -First 1 -ExpandProperty Id
+
+if (($processHandle = [impsys.win32]::OpenProcess(
+ 0x400,
+ $true,
+ [Int32]$winlogonPid)) -eq [IntPtr]::Zero) {
+ $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ Write-Error "$([ComponentModel.Win32Exception]$err)"
+ Exit $err
+}
+
+$tokenHandle = [IntPtr]::Zero
+if (-not [impsys.win32]::OpenProcessToken(
+ $processHandle,
+ 0x0E,
+ [ref]$tokenHandle)) {
+ $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ Write-Error "$([ComponentModel.Win32Exception]$err)"
+ Exit $err
+}
+
+$dupTokenHandle = [IntPtr]::Zero
+if (-not [impsys.win32]::DuplicateTokenEx(
+ $tokenHandle,
+ 0x02000000,
+ [IntPtr]::Zero,
+ 0x02,
+ 0x01,
+ [ref]$dupTokenHandle)) {
+ $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ Write-Error "$([ComponentModel.Win32Exception]$err)"
+ Exit $err
+}
+
+if (-not [impsys.win32]::ImpersonateLoggedOnUser(
+ $dupTokenHandle)) {
+ $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
+ Write-Error "$([ComponentModel.Win32Exception]$err)"
+ Exit $err
+}
+###################################### SUBLICENSE END ######################################
+
+Add-Type -AssemblyName System.Security
+$key = "registry::HKEY_USERS\S-1-5-19\Software\Microsoft\IdentityCRL\Immersive\production\Token\{D6D5A677-0872-4AB0-9442-BB792FCE85C5}"
+$ticket = (Get-ItemProperty -Path $key)."DeviceTicket"
+$raw = ([Text.Encoding]::Unicode).GetString([Security.Cryptography.ProtectedData]::Unprotect($ticket[4..$ticket.length], $Null, [Security.Cryptography.DataProtectionScope]::LocalMachine)) -replace "^.*?t\=" -replace "\&p\=.*"
+
+Set-Content -NoNewline -Path dev_tik.txt -Value "$raw"
diff --git a/kh_editlic.py b/kh_editlic.py new file mode 100644 index 0000000..15275a3 --- /dev/null +++ b/kh_editlic.py @@ -0,0 +1,70 @@ +import sys
+import random
+import struct
+import time
+import binascii
+import hashlib
+
+tlv_types = {
+ "SignedBlock": 0x14,
+ "DeviceLicenseExpirationTime": 0x1f,
+ "PollingTime": 0xd3,
+ "LicenseExpirationTime": 0x20,
+ "ClepSignState": 0x12d,
+ "LicenseDeviceId": 0xd2,
+ "UnkBlock1": 0xd1,
+ "LicenseId": 0xcb,
+ "HardwareId": 0xd0,
+ "UnkBlock2": 0xcf,
+ "UplinkKeyId": 0x18,
+ "UnkBlock3": 0x0,
+ "UnkBlock4": 0x12e,
+ "UnkBlock5": 0xd5,
+ "PackageFullName": 0xce,
+ "LicenseInformation": 0xc9,
+ "PackedContentKeys": 0xca,
+ "EncryptedDeviceKey": 0x1,
+ "DeviceLicenseDeviceId": 0x2,
+ "LicenseEntryIds": 0xcd,
+ "LicensePolicies": 0xd4,
+ "KeyholderPublicSigningKey": 0xdc,
+ "KeyholderPolicies": 0xdd,
+ "KeyholderKeyLicenseId": 0xde,
+ "SignatureBlock": 0xcc,
+};
+
+def encode_tlvblock(type, data):
+ return struct.pack("<II", tlv_types[type], len(data)) + data
+
+extradata = None
+
+lic_file = sys.argv[1]
+new_pfn = sys.argv[2].lower()
+out_lic = sys.argv[3]
+
+if len(sys.argv) >= 5:
+ extra_file = sys.argv[4]
+
+ with open(extra_file, "rb") as f:
+ extradata = f.read()
+
+with open(lic_file, "rb") as f:
+ data = f.read()
+
+data += encode_tlvblock("PackageFullName", new_pfn.encode("utf-16-le") + b"\x00\x00")
+
+basic_pol = 0x0a
+if "addon" in new_pfn:
+ basic_pol = 0x00
+
+lic_info = struct.pack("<HHIBB", 5, 1, int(time.time()), basic_pol, 1)
+data += encode_tlvblock("LicenseInformation", lic_info)
+data += encode_tlvblock("LicenseId", random.randbytes(16))
+data += encode_tlvblock("LicenseEntryIds", b"\x01\x00" + hashlib.sha256(new_pfn.encode("utf-16-le")).digest())
+data += encode_tlvblock("LicenseExpirationTime", b"\x00\x00\x00\x00")
+
+if extradata:
+ data += extradata
+
+with open(out_lic, "wb") as f:
+ f.write(data)
\ No newline at end of file diff --git a/resignlic.py b/resignlic.py new file mode 100644 index 0000000..12dd328 --- /dev/null +++ b/resignlic.py @@ -0,0 +1,125 @@ +from bs4 import BeautifulSoup
+from xml.etree import ElementTree as ET
+from base64 import b64decode, b64encode
+from datetime import datetime
+from time import time
+from Crypto.PublicKey import RSA
+from Crypto.Hash import SHA256
+from Crypto.Signature import PKCS1_v1_5
+from ecdsa import SigningKey
+from copy import copy
+import argparse, re
+
+# TODO: Implement SPLicenseBlock parser
+
+CLIPUP_ECC_KEY_PEM = """-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPJp69Tr9nAvAHi3B
+2dr5jenY4MkTwy4L/ahplSNxvgahRANCAATC/nluDYXrQHgi9STrd2kEhS4cKfTD
+URm5vYGwUG1Jxva69OJEUiF2sfMhYGnCDYHrLM5ndcA0s43ie1+z3y1t
+-----END PRIVATE KEY-----"""
+
+clipup_eckey = SigningKey.from_pem(CLIPUP_ECC_KEY_PEM, hashfunc=SHA256.new)
+
+def xml_open(xmlf):
+ with open(xmlf) as f:
+ cont = f.read()
+
+ return BeautifulSoup(cont, "xml")
+
+def xml_save(xml, path):
+ with open(path, "wb") as f:
+ f.write(canonicb(xml))
+
+def iso_date(t):
+ return datetime.fromtimestamp(t).isoformat() + "Z"
+
+def sha256(s):
+ return SHA256.new(s).digest()
+
+def b64sencode(s):
+ return b64encode(s).decode()
+
+def b64ssencode(s):
+ return b64sencode(s.encode())
+
+def b64sdecode(s):
+ return b64decode(s).decode()
+
+def hashb64(s):
+ return b64sencode(sha256(s))
+
+def canonicb(x):
+ return ET.canonicalize(str(x), strip_text=True).encode()
+
+def decode_tsl(tsl, log=False):
+ if tsl.SPLicenseBlock:
+ licblock = b64decode(tsl.SPLicenseBlock.text)
+ else:
+ licblock = None
+
+ pubkey = clipup_eckey.get_verifying_key()
+
+ sig_inf = tsl.SignedInfo
+ sig_inf.attrs["xmlns"] = "http://www.w3.org/2000/09/xmldsig#"
+ sig_val = b64decode(tsl.SignatureValue.text)
+ sig_data = canonicb(sig_inf)
+
+ try:
+ valid = pubkey.verify(sig_val, sig_data)
+ except:
+ valid = False
+
+ if log:
+ print("TSL Information:")
+ print(f"Valid: {valid}")
+
+ return licblock, valid
+
+def resign_lic(tsl, licblock=None):
+ sig_tag = copy(tsl.License.Signature)
+ tsl.License.Signature.decompose()
+
+ if licblock:
+ if tsl.License.SPLicenseBlock is None:
+ tsl.License.append(tsl.new_tag("SPLicenseBlock"))
+
+ tsl.SPLicenseBlock.string = b64sencode(licblock)
+
+
+ hash = hashb64(canonicb(tsl))
+ sig_tag.SignedInfo.DigestValue.string = hash
+
+ sig_tag.SignedInfo.attrs["xmlns"] = "http://www.w3.org/2000/09/xmldsig#"
+ sig = b64sencode(clipup_eckey.sign_deterministic(canonicb(sig_tag.SignedInfo)))
+ del sig_tag.SignedInfo.attrs["xmlns"]
+
+ sig_tag.SignatureValue.string = sig
+
+ tsl.License.append(sig_tag)
+
+ return tsl
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("mode", help="Mode: encode, decode")
+ parser.add_argument("license", nargs="?", default="", help="Data/path")
+ parser.add_argument("licblock", help="Type: tslraw, tslconv, ticket")
+ parser.add_argument("--output", "-o", help="Output path", default="edit.xml")
+ args = parser.parse_args()
+
+ licblock = None
+
+ tsl = xml_open(args.license)
+
+ if args.mode == "encode":
+ with open(args.licblock, "rb") as f:
+ licblock = f.read()
+
+ tsl = resign_lic(tsl, licblock)
+ xml_save(tsl, args.output)
+ elif args.mode == "decode":
+ licblock_out, valid = decode_tsl(tsl, log=False)
+
+ if licblock_out and args.licblock:
+ with open(args.licblock, "wb") as f:
+ f.write(licblock_out)
\ No newline at end of file diff --git a/splicenseblock.hexpat b/splicenseblock.hexpat new file mode 100644 index 0000000..26086b7 --- /dev/null +++ b/splicenseblock.hexpat @@ -0,0 +1,142 @@ +#include <std/mem.pat> + +struct UTF16CStr { + char16 data[while(std::mem::read_unsigned($, 2) != 0x0)]; + char16 terminator[[hidden]]; +}; + +enum LicenseType : u16 { + Unknown = 0, + App = 1, + Lease = 2, + Device = 3, + Dev = 4, + Lob = 5, + Upgrade = 6, +}; + +bitfield BasicPolicies { + lease_required : 1; + is_primary : 1; + expired : 1; + is_device_locked : 1; + padding : 12; +}; + +struct LicenseInformation { + u16 version; + LicenseType type; + s32 issued; + BasicPolicies policies; +}; + +// SHA256 of PFN (lowercase) +struct LicenseEntryId { + u8 data[32]; +}; + +enum PolicyType : u8 { + NONE = 0x01, + STRING = 0x11, + BINARY = 0x31, + DWORD = 0x41, + MULTI_SZ = 0x71 +}; + +struct Policy { + u16 something1[[hidden]]; + u16 something2[[hidden]]; + u8 pad0[[hidden]]; + PolicyType type; + u16 pad1[[hidden]]; + u16 priority; + u16 name_sz; + u16 data_sz; + char16 name[name_sz / 2]; + + u32 end = $ + data_sz; + match (type) { + (PolicyType::STRING): { + UTF16CStr data; + } + (PolicyType::DWORD): { + u32 data; + } + (PolicyType::MULTI_SZ): { + UTF16CStr data[]; + } + (_): { + u8 data[while($ < end)]; + } + } + u16 terminator[[hidden]]; +}; + +enum BlockType : u32 { + SignedBlock = 0x14, + DeviceLicenseExpirationTime = 0x1f, + PollingTime = 0xd3, + LicenseExpirationTime = 0x20, + ClepSignState = 0x12d, + LicenseDeviceId = 0xd2, + UnkBlock1 = 0xd1, + LicenseId = 0xcb, + HardwareId = 0xd0, + UnkBlock2 = 0xcf, + UplinkKeyId = 0x18, + UnkBlock3 = 0x0, + UnkBlock4 = 0x12e, + UnkBlock5 = 0xd5, + PackageFullName = 0xce, + LicenseInformation = 0xc9, + PackedContentKeys = 0xca, + EncryptedDeviceKey = 0x1, + DeviceLicenseDeviceId = 0x2, + LicenseEntryIds = 0xcd, + LicensePolicies = 0xd4, + KeyholderPublicSigningKey = 0xdc, + KeyholderPolicies = 0xdd, + KeyholderKeyLicenseId = 0xde, + SignatureBlock = 0xcc, +}; + + + + +struct TLVBlock { + BlockType type; + u32 size; + + u32 end = $ + size; + match (type) { + (BlockType::SignedBlock): { + TLVBlock block[while($ < end)]; + } + (BlockType::DeviceLicenseExpirationTime | BlockType::PollingTime | BlockType::LicenseExpirationTime): { + s32 time; + } + (BlockType::PackageFullName): { + UTF16CStr name; + } + (BlockType::LicenseInformation): { + LicenseInformation information; + } + (BlockType::LicenseEntryIds): { + u16 count[[hidden]]; + LicenseEntryId ids[count]; + } + (BlockType::LicensePolicies): { + Policy policies[while($ < end)]; + } + (BlockType::SignatureBlock): { + u16 something0[[hidden]]; + u16 origin; + u8 data[while($ < end)]; + } + (_): { u8 data[size]; } + } +}; + + + +TLVBlock blocks[while($ < std::mem::size())] @ 0;
\ No newline at end of file |
