Skip to content

Commit 66d54e7

Browse files
authored
Merge pull request #10 from LukeFZ/dev
Update to .NET 8 + Support 24H2
2 parents a0570fe + cecc30e commit 66d54e7

File tree

3 files changed

+155
-104
lines changed

3 files changed

+155
-104
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
strategy:
1515
matrix:
16-
dotnet-version: [ '6.0.x' ]
16+
dotnet-version: [ '8.0.x' ]
1717
rid: ['win-x64']
1818

1919
steps:

CikExtractor/CikExtractor.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0-windows</TargetFramework>
5+
<TargetFramework>net8.0-windows7.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
12-
<PackageReference Include="Registry" Version="1.3.4" />
13-
<PackageReference Include="Spectre.Console.Cli" Version="0.47.1-preview.0.11" />
14-
<PackageReference Include="System.Management" Version="6.0.0" />
11+
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
12+
<PackageReference Include="Registry" Version="1.5.0" />
13+
<PackageReference Include="Spectre.Console.Cli" Version="0.49.2-preview.0.75" />
14+
<PackageReference Include="System.Management" Version="9.0.2" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

CikExtractor/Emulation/clep_vault.py

Lines changed: 149 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -7,105 +7,156 @@
77

88
import argparse
99

10+
1011
@winsdkapi(cc=STDCALL)
1112
def hook_chkstk(ql, addr, params):
12-
return ql.arch.regs.rax
13+
return ql.arch.regs.rax
14+
1315

1416
def parse_args():
15-
parser = argparse.ArgumentParser(description="decrypts a ClepV4 encrypted device key.")
16-
parser.add_argument("--license", required=True, help="base64 encoded encrypted device license (Required length: 4094)")
17-
parser.add_argument("--smbios", required=True, help="base64 encoded SMBIOS system struct.")
18-
parser.add_argument("--driveser", required=True, help="base64 encoded null-terminated root drive serial number.")
19-
args = parser.parse_args()
20-
return (args.license, args.smbios, args.driveser)
21-
22-
if __name__ == '__main__':
23-
enc_license_b64, smbios_b64, driveser_b64 = parse_args()
24-
encrypted_device_license = b64decode(enc_license_b64)
25-
smbiosSystem = b64decode(smbios_b64)
26-
driveSer = b64decode(driveser_b64)
27-
28-
assert len(encrypted_device_license) == 4094, "Error: Encrypted Device License length mismatch. (Expected: 4094)"
29-
30-
max_smbios = 256
31-
max_driveser = 64
32-
max_tpminfo = 901
33-
34-
if (len(smbiosSystem) > max_smbios):
35-
smbiosSystem = smbiosSystem[:max_smbios]
36-
37-
if (len(driveSer) > max_driveser):
38-
driveSer = driveSer[:max_driveser]
39-
40-
ql = Qiling(["./clipsp.sys"], ".\\x8664_windows", libcache=True, console=False)
41-
42-
ql.os.set_api("__chkstk", hook_chkstk)
43-
44-
clep_vault_func_pattern = b"\x4C\x8B\xDC\x49\x89\x4B\x08"
45-
clep_vault_size = 0x4e
46-
clep_vault_func = ql.mem.search(clep_vault_func_pattern, begin=0x1c0000000, end=0x1d0000000)[0]
47-
if clep_vault_func is None:
48-
print("Error: Failed to find vault function using pattern.")
49-
exit()
50-
51-
# Gets the cached request memory location through a bit of trickery:
52-
# Basically pattern matching a part of a vault function, navigating to the lea request opcode, then reading the operand for the offset
53-
clep_request_pattern = b"\xC6\x45\x00\x19\x8A\x45\x00\x8B\x04\x24\x48\x83\xEC\x10\x8B\x04\x24\x8B\x04\x24\x48\x83\xEC\x50\x48\x8D\x4C\x24\x20\x8B\x01\x41\x0F\x10\x02\x33\xC0\x48\x8D\x59\x0F\x48\x83\xE3\xF0\xF3\x0F\x7F\x43\x28"
54-
clep_request_opcode_offset = 0x4e # actually 0x4b, but + 3 to get the operand directly
55-
clep_request_func = ql.mem.search(clep_request_pattern, begin=0x1c0000000, end=0x1d0000000)[0]
56-
if clep_request_func is None:
57-
print("Error: Failed to find request function using pattern.")
58-
exit()
59-
60-
clep_request_opcode = clep_request_func + clep_request_opcode_offset
61-
clep_request_ptr_offset = struct.unpack("<I", ql.mem.read(clep_request_opcode, 4))[0]
62-
clep_request_ptr = clep_request_opcode + clep_request_ptr_offset + 4 # To offset the operand size
63-
64-
req_version = clep_request_ptr
65-
req_smbios = req_version + 4
66-
req_driveser = req_smbios + max_smbios
67-
req_tpmstatus = req_driveser + max_driveser
68-
req_tpminfo = req_tpmstatus + 1
69-
req_istogo = req_tpminfo + max_tpminfo
70-
req_debuggerEnabled = req_istogo + 1
71-
req_debuggerAttached = req_debuggerEnabled + 4
72-
req_remdata = req_debuggerAttached + 4
73-
74-
ql.mem.write(req_version, struct.pack("<I", 0x4))
75-
ql.mem.write(req_smbios, smbiosSystem)
76-
ql.mem.write(req_driveser, driveSer)
77-
78-
ql.mem.write(req_tpmstatus, struct.pack("<B", 0x0))
79-
ql.mem.write(req_istogo, struct.pack("<B", 0x0))
80-
81-
ql.mem.write(req_debuggerEnabled, struct.pack("<I", 0x7ffe02d4))
82-
ql.mem.write(req_debuggerAttached, struct.pack("<I", 0x7ffe02d4))
83-
84-
try:
85-
ql.os.KUSER_SHARED_DATA
86-
except:
87-
# We are running in a Qiling version that does not have KUSER_SHARED_DATA, set debugger var manually
88-
ql.mem.map(0x7ffe02d4 // 4096*4096, 4096)
89-
ql.mem.write(0x7ffe02d4, struct.pack("<I", 0x00000010)) # 0x0 - Debugger not enabled | 0x10 - Debugger not attached
90-
91-
pb_secret = ql.os.heap.alloc(32)
92-
license_buffer = ql.os.heap.alloc(len(encrypted_device_license))
93-
ql.mem.write(license_buffer, encrypted_device_license)
94-
95-
# sp_cache = 0x1C0043600 --- If needed this could also be done through pattern matching, but we can just use our own buffer
96-
sp_cache = ql.os.heap.alloc(64)
97-
98-
#clep_vault_v4_begin = 0x1C000B234
99-
#clep_vault_v4_end = 0x1C000B282
100-
101-
ql.arch.regs.rcx = 0x0
102-
ql.arch.regs.rdx = license_buffer + 4
103-
ql.arch.regs.r8 = pb_secret
104-
ql.arch.regs.r9 = license_buffer + 516
105-
106-
ql.stack_write(0x30, sp_cache)
107-
108-
ql.run(begin=clep_vault_func, end=clep_vault_func + clep_vault_size)
109-
110-
buffer = ql.mem.read(pb_secret, 16)
111-
print(binascii.hexlify(buffer).decode("utf-8"))
17+
parser = argparse.ArgumentParser(
18+
description="decrypts a ClepV4 encrypted device key."
19+
)
20+
parser.add_argument(
21+
"--license",
22+
required=True,
23+
help="base64 encoded encrypted device license (Required length: 4094)",
24+
)
25+
parser.add_argument(
26+
"--smbios", required=True, help="base64 encoded SMBIOS system struct."
27+
)
28+
parser.add_argument(
29+
"--driveser",
30+
required=True,
31+
help="base64 encoded null-terminated root drive serial number.",
32+
)
33+
args = parser.parse_args()
34+
return (args.license, args.smbios, args.driveser)
35+
36+
37+
if __name__ == "__main__":
38+
enc_license_b64, smbios_b64, driveser_b64 = parse_args()
39+
encrypted_device_license = b64decode(enc_license_b64)
40+
smbiosSystem = b64decode(smbios_b64)
41+
driveSer = b64decode(driveser_b64)
42+
43+
assert len(encrypted_device_license) == 4094, (
44+
"Error: Encrypted Device License length mismatch. (Expected: 4094)"
45+
)
46+
47+
max_smbios = 256
48+
max_driveser = 64
49+
max_tpminfo = 901
50+
51+
if len(smbiosSystem) > max_smbios:
52+
smbiosSystem = smbiosSystem[:max_smbios]
53+
54+
if len(driveSer) > max_driveser:
55+
driveSer = driveSer[:max_driveser]
56+
57+
ql = Qiling(["./clipsp.sys"], ".\\x8664_windows", libcache=True, console=False)
58+
59+
ql.os.set_api("__chkstk", hook_chkstk)
60+
61+
clep_vault_func_pattern = b"\x4c\x8b\xdc\x49\x89\x4b\x08"
62+
clep_vault_size = 0x4E
63+
clep_vault_func = ql.mem.search(
64+
clep_vault_func_pattern, begin=0x1C0000000, end=0x1D0000000
65+
)[0]
66+
if clep_vault_func is None:
67+
print("Error: Failed to find vault function using pattern.")
68+
exit()
69+
70+
# Gets the cached request memory location through a bit of trickery:
71+
# Basically pattern matching a part of a vault function, navigating to the lea request opcode, then reading the operand for the offset
72+
# tuple of (pattern, offset to address)
73+
patterns_to_try = [
74+
# actually 0x4b, but + 3 to get the operand directly
75+
(
76+
b"\xc6\x45\x00\x19\x8a\x45\x00\x8b\x04\x24\x48\x83\xec\x10\x8b\x04\x24\x8b\x04\x24\x48\x83\xec\x50\x48\x8d\x4c\x24\x20\x8b\x01\x41\x0f\x10\x02\x33\xc0\x48\x8d\x59\x0f\x48\x83\xe3\xf0\xf3\x0f\x7f\x43\x28",
77+
0x4E,
78+
),
79+
(b"\xc6\x45\x00\x19\x0f\xb6\x45", 0x50),
80+
]
81+
82+
clep_request_ptr = 0
83+
84+
for pattern, offset in patterns_to_try:
85+
results = ql.mem.search(pattern, begin=0x1C0000000, end=0x1D0000000)
86+
if len(results) == 0:
87+
continue
88+
89+
if len(results) != 1:
90+
print(
91+
"Error: Ambiguous request function references found: "
92+
+ str(hex(x) for x in results)
93+
)
94+
continue
95+
96+
clep_request_opcode = results[0] + offset
97+
clep_request_ptr_offset = struct.unpack(
98+
"<I", ql.mem.read(clep_request_opcode, 4)
99+
)[0]
100+
101+
clep_request_ptr = (
102+
clep_request_opcode + clep_request_ptr_offset + 4
103+
) # To offset the operand size
104+
105+
break
106+
107+
if clep_request_ptr == 0:
108+
print("Error: Failed to find request location using pattern.")
109+
exit()
110+
111+
req_version = clep_request_ptr
112+
req_smbios = req_version + 4
113+
req_driveser = req_smbios + max_smbios
114+
req_tpmstatus = req_driveser + max_driveser
115+
req_tpminfo = req_tpmstatus + 1
116+
req_istogo = req_tpminfo + max_tpminfo
117+
req_debuggerEnabled = req_istogo + 1
118+
req_debuggerAttached = req_debuggerEnabled + 4
119+
req_remdata = req_debuggerAttached + 4
120+
121+
ql.mem.write(req_version, struct.pack("<I", 0x4))
122+
ql.mem.write(req_smbios, smbiosSystem)
123+
ql.mem.write(req_driveser, driveSer)
124+
125+
ql.mem.write(req_tpmstatus, struct.pack("<B", 0x0))
126+
ql.mem.write(req_istogo, struct.pack("<B", 0x0))
127+
128+
ki_debugger_addr = 0x7FFE02D4
129+
130+
ql.mem.write(req_debuggerEnabled, struct.pack("<I", ki_debugger_addr))
131+
ql.mem.write(req_debuggerAttached, struct.pack("<I", ki_debugger_addr))
132+
133+
try:
134+
ql.os.KUSER_SHARED_DATA
135+
except Exception:
136+
# We are running in a Qiling version that does not have KUSER_SHARED_DATA, set debugger var manually
137+
ql.mem.map(ki_debugger_addr // 4096 * 4096, 4096)
138+
ql.mem.write(
139+
ki_debugger_addr, struct.pack("<I", 0x00000010)
140+
) # 0x0 - Debugger not enabled | 0x10 - Debugger not attached
141+
142+
pb_secret = ql.os.heap.alloc(32)
143+
license_buffer = ql.os.heap.alloc(len(encrypted_device_license))
144+
ql.mem.write(license_buffer, encrypted_device_license)
145+
146+
# sp_cache = 0x1C0043600 --- If needed this could also be done through pattern matching, but we can just use our own buffer
147+
sp_cache = ql.os.heap.alloc(64)
148+
149+
# clep_vault_v4_begin = 0x1C000B234
150+
# clep_vault_v4_end = 0x1C000B282
151+
152+
ql.arch.regs.rcx = 0x0
153+
ql.arch.regs.rdx = license_buffer + 4
154+
ql.arch.regs.r8 = pb_secret
155+
ql.arch.regs.r9 = license_buffer + 516
156+
157+
ql.stack_write(0x30, sp_cache)
158+
159+
ql.run(begin=clep_vault_func, end=clep_vault_func + clep_vault_size)
160+
161+
buffer = ql.mem.read(pb_secret, 16)
162+
print(binascii.hexlify(buffer).decode("utf-8"))

0 commit comments

Comments
 (0)