This repository has been archived by the owner on Nov 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 67
/
patchsmc
executable file
·389 lines (300 loc) · 10.4 KB
/
patchsmc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#!/usr/bin/env python3
# coding=utf-8
# SPDX-License-Identifier: MIT
"""
vSMC Header Structure
=====================
Offset Length Struct Type Description
----------------------------------------
0x00/00 0x08/08 Q ptr Offset to key table
0x08/08 0x04/4 I int Number of private keys
0x0C/12 0x04/4 I int Number of public keys
vSMC Key Data Structure
Offset Length Struct Type Description
----------------------------------------
0x00/00 0x04/04 4s int Key name (byte reversed e.g. #KEY is YEK#)
0x04/04 0x01/01 B byte Length of returned data
0x05/05 0x04/04 4s int Data type (byte reversed e.g. ui32 is 23iu)
0x09/09 0x01/01 B byte Flag R/W
0x0A/10 0x06/06 6x byte Padding
0x10/16 0x08/08 Q ptr Internal VMware routine
0x18/24 0x30/48 48B byte Data
The internal VMware routines point to 4 variants:
AppleSMCHandleDefault
AppleSMCHandleNTOK
AppleSMCHandleNumKeys
AppleSMCHandleOSK
"""
import codecs
import os
import struct
import sys
# Constants for header and key access
HDR_PACK = '=QII'
HDR_LENGTH = 16
KEY_PACK = '=4sB4sB6xQ'
KEY_LENGTH = 24
DATA_LENGTH = 48
ROW_LENGTH = KEY_LENGTH + DATA_LENGTH
# Setup hex string for vSMC headers
# These are the private and public key counts
SMC_HEADER_V0 = b'\xF2\x00\x00\x00\xF0\x00\x00\x00'
SMC_HEADER_V1 = b'\xB4\x01\x00\x00\xB0\x01\x00\x00'
# Keys we use
KEY_KEY = b'YEK#'
LKS_KEY = b'SKL+'
OSK0_KEY = b'0KSO'
OSK1_KEY = b'1KSO'
# Haiku
OSK0 = codecs.encode('bheuneqjbexolgurfrjbeqfthneqrqcy', 'rot_13').encode('UTF-8')
OSK1 = codecs.encode('rnfrqbagfgrny(p)NccyrPbzchgreVap', 'rot_13').encode('UTF-8')
# ELF Magic
ELF_MAGIC = b'\x7fELF'
if sys.version_info < (3, 6):
sys.stderr.write('You need Python 3.6 or later\n')
sys.exit(1)
def bytetohex(data):
return ''.join('{:02X}'.format(c) for c in data)
def printhdr(offset, hdr):
print(f'File Offset : 0x{offset:08x}')
print(f'Keys Offset : 0x{hdr[0]:08x}')
print(f'Private Key #: 0x{hdr[1]:04x}/{hdr[1]:04d}')
print(f'Public Keys #: 0x{hdr[2]:04x}/{hdr[2]:04d}')
print()
return
def printkey(offset, smc_key, smc_data):
# Format smc_type as cannot use \ in f-strings
smc_type = smc_key[2][::-1].replace(b'\x00', b' ').decode('UTF-8')
print(f'0x{offset:08x} '
f'{smc_key[0][::-1].decode("UTF-8")} '
f'{smc_key[1]:03d} '
f'{smc_type} '
f'0x{smc_key[3]:02x} '
f'0x{smc_key[4]:08x} '
f'{bytetohex(smc_data)}')
return
def gethdr(vmx, offset):
# Read header into struct
vmx.seek(offset)
hdr = struct.unpack(HDR_PACK, vmx.read(HDR_LENGTH))
vmx.seek(offset)
return hdr
def getkey(vmx, offset):
# Read key into struct
vmx.seek(offset)
smc_key = struct.unpack(KEY_PACK, vmx.read(KEY_LENGTH))
vmx.seek(offset)
return smc_key
def setkey(vmx, offset, smc_key):
# Write key from struct
vmx.seek(offset)
vmx.write(struct.pack(KEY_PACK, smc_key[0], smc_key[1], smc_key[2], smc_key[3], smc_key[4]))
vmx.flush()
vmx.seek(offset)
return
def getdata(vmx, offset, smc_key):
# Read data for key
vmx.seek(offset + KEY_LENGTH)
smc_data = vmx.read(smc_key[1])
vmx.seek(offset)
return smc_data
def setdata(vmx, offset, smc_data):
# Write data for key
vmx.seek(offset + KEY_LENGTH)
vmx.write(smc_data)
vmx.flush()
vmx.seek(offset)
return
def patchosk(vmx, offset, ptr, data):
# Get the OSK key and data
smc_key = getkey(vmx, offset)
smc_data = getdata(vmx, offset, smc_key)
key = smc_key[0][::-1].decode('UTF-8')
smc_osk_ptr = smc_key[4]
print(f'{key} Key Before:')
printkey(offset, smc_key, smc_data)
# AppleSMCHandleOSK replaced with AppleSMCHandleDefault
temp = list(smc_key)
temp[4] = ptr
smc_key = tuple(temp)
setkey(vmx, offset, smc_key)
# Set the data value
setdata(vmx, offset, data)
# Get patched OSK key and data
smc_key = getkey(vmx, offset)
smc_data = getdata(vmx, offset, smc_key)
print(f'{key} Key After:')
printkey(offset, smc_key, smc_data)
return smc_osk_ptr
def patchsmc(name):
with open(name, 'rb+') as vmx:
# Load vmx file
vmx_bytes = vmx.read()
# Find the vSMC headers
smc0_header = vmx_bytes.find(SMC_HEADER_V0) - 8
smc1_header = vmx_bytes.find(SMC_HEADER_V1) - 8
# Find '#KEY' keys
smc0_key = vmx_bytes.find(KEY_KEY)
smc1_key = vmx_bytes.rfind(KEY_KEY)
# Find '+LKS' key
smc0_lks = vmx_bytes.find(LKS_KEY, smc0_key)
smc1_lks = vmx_bytes.find(LKS_KEY, smc1_key)
# Find 'OSK0' keys
smc0_osk0 = vmx_bytes.find(OSK0_KEY, smc0_key)
smc1_osk0 = vmx_bytes.find(OSK0_KEY, smc1_key)
# Find 'OSK1' keys
smc0_osk1 = vmx_bytes.find(OSK1_KEY, smc0_key)
smc1_osk1 = vmx_bytes.find(OSK1_KEY, smc1_key)
# Check to see if we have already patched the vSMC in the file
osk0 = getdata(vmx, smc1_osk0, getkey(vmx, smc1_osk0))
osk1 = getdata(vmx, smc1_osk1, getkey(vmx, smc1_osk1))
if osk0 == OSK0 and osk1 == OSK1:
print(f'File {name} is already patched')
return
# Patch first vSMC table
print('\nappleSMCTableV0 (smc.version = "0")')
hdr = gethdr(vmx, smc0_header)
printhdr(smc0_header, hdr)
# Get the +LKS key data routine for OSK0/1
smc_key = getkey(vmx, smc0_lks)
smc_default_ptr = smc_key[4]
# Patch OSK0 key
patchosk(vmx, smc0_osk0, smc_default_ptr, OSK0)
# Patch OSK1 key
patchosk(vmx, smc0_osk1, smc_default_ptr, OSK1)
# Patch second vSMC table
print('\nappleSMCTableV1 (smc.version = "1")')
hdr = gethdr(vmx, smc1_header)
printhdr(smc1_header, hdr)
# Get the +LKS key data routine for OSK0/1
smc_key = getkey(vmx, smc1_lks)
smc_default_ptr = smc_key[4]
# Patch OSK0 key
patchosk(vmx, smc1_osk0, smc_default_ptr, OSK0)
# Patch OSK1 key & get the output for ELF patching
smc_osk_ptr = patchosk(vmx, smc1_osk1, smc_default_ptr, OSK1)
# Patch relocation records if ELF executable
vmx.seek(0)
magic = vmx.read(4)
if magic == ELF_MAGIC:
# Find matching RELA record in .rela.dyn in ELF files
print(f'\nModifying ELF RELA records from 0x{smc_osk_ptr:08x} -> 0x{smc_default_ptr:08x}')
# Repack ints to bytes for find
packed_old_ptr = struct.pack('=Q', smc_osk_ptr)
packed_new_ptr = struct.pack('=Q', smc_default_ptr)
# Find first 4 smc_osk_ptr from RELA
offset = 0
for i in range(0, 4):
offset = vmx_bytes.find(packed_old_ptr, offset)
print(f'Relocation modified at: 0x{offset:08x}')
vmx.seek(offset)
vmx.write(packed_new_ptr)
offset += 1
print('\n')
# Tidy up
del vmx_bytes
vmx.flush()
vmx.close()
def dumpkeys(vmx, offset, count):
print(f'Table Offset : 0x{offset:08x}')
print('Offset Name Len Type Flag FuncPtr Data')
print('------- ---- --- ---- ---- ------- ----')
for i in range(count):
# Read key into struct str and data byte str
smc_key = getkey(vmx, offset)
smc_data = getdata(vmx, offset, smc_key)
# Dump entry
printkey(offset, smc_key, smc_data)
offset = offset + ROW_LENGTH
return
def dumpsmc(name):
with open(name, 'rb') as vmx:
# Memory map file
vmx_bytes = vmx.read()
# Find the vSMC headers
smc0_header = vmx_bytes.find(SMC_HEADER_V0) - 8
smc1_header = vmx_bytes.find(SMC_HEADER_V1) - 8
# Find '#KEY' keys
smc0_key = vmx_bytes.find(KEY_KEY, smc0_header)
smc1_key = vmx_bytes.find(KEY_KEY, smc1_header)
# Free bytes from mem
del vmx_bytes
# Dump first vSMC table
print('\nappleSMCTableV0 (smc.version = "0")')
hdr = gethdr(vmx, smc0_header)
printhdr(smc0_header, hdr)
dumpkeys(vmx, smc0_key, hdr[1])
# Dump second vSMC table
print('\nappleSMCTableV1 (smc.version = "1")')
hdr = gethdr(vmx, smc1_header)
printhdr(smc1_header, hdr)
dumpkeys(vmx, smc1_key, hdr[1])
# Tidy up
vmx.close()
return
def checksmc(name):
with open(name, 'rb') as vmx:
# Memory map file
vmx_bytes = vmx.read()
# Find '#KEY' keys
smc1_key = vmx_bytes.rfind(KEY_KEY)
# Find 'OSK0' keys
smc1_osk0 = vmx_bytes.find(OSK0_KEY, smc1_key)
# Find 'OSK1' keys
smc1_osk1 = vmx_bytes.find(OSK1_KEY, smc1_key)
# Free bytes from mem
del vmx_bytes
# Check to see if we have already patched the vSMC in the file
osk0 = getdata(vmx, smc1_osk0, getkey(vmx, smc1_osk0))
osk1 = getdata(vmx, smc1_osk1, getkey(vmx, smc1_osk1))
if osk0 == OSK0 and osk1 == OSK1:
flag = True
else:
flag = False
print(f'Patch Status: {flag}')
# Tidy up
vmx.close()
return
def print_usage():
print('Usage:')
print(f'{sys.argv[0]} patch|dump|check <vmx filename>')
print('\nFirst option is command to perform:')
print(f' patch - patch the vSMC table in vmx executable')
print(f' check - check the vSMC table in vmx executable')
print(f' dump - dump the vSMC table in vmx executable')
print(f'\nSecond option is the filename of executable:')
print(f' <vmx filename> - the vmx executable to patch (vmx/vmx-debug/vmx-stats)')
sys.exit(1)
def main():
# Function pointer
f = None
# Parse args and call command
if len(sys.argv) >= 3:
if sys.argv[1] == 'check':
f = checksmc
elif sys.argv[1] == 'dump':
f = dumpsmc
elif sys.argv[1] == 'patch':
f = patchsmc
elif sys.argv[1] == '-h':
print_usage()
else:
print(f'Error: Incorrect command {sys.argv[1]} passed.')
print_usage()
filename = sys.argv[2]
if os.path.isfile(filename):
print(f'Filename: {filename}')
f(filename)
else:
print(f'Error: Cannot find file {filename}')
sys.exit(1)
return
else:
print_usage()
return
if __name__ == '__main__':
print('PatchSMC 4.0.5')
print('==============')
print()
main()
sys.exit(0)