Repository: shanyungyang/esxi-unlocker
Branch: master
Commit: 9f3483ef6780
Files: 11
Total size: 25.9 KB
Directory structure:
gitextract_zmf1ry0m/
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── main.yml
│ └── manual.yml
├── .gitignore
├── LICENSE
├── esxi-build.py
├── esxi-install.sh
├── esxi-smctest.sh
├── esxi-uninstall.sh
├── etc/
│ └── rc.local.d/
│ └── unlocker.py
└── readme.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Recommedations from GitHub help pages
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Declare files that will always have CRLF line endings on checkout.
*.cmd text eol=crlf
# Declare files that will always have LF line endings on checkout.
*.sh text eol=lf
*.py text eol=lf
# Declare files that are binary on checkout.
*.exe binary
================================================
FILE: .github/workflows/main.yml
================================================
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
push:
# branches: [ master ]
tags:
- '[0-9].[0-9]+.[0-9]+'
pull_request:
branches: [ master ]
tags:
- '[0-9].[0-9]+.[0-9]+'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
# Runs a few command using the runners shell
- name: Run a multi-line script
run: |
# pwd debug
tar -cvzf unlocker.tgz etc
tar -cvzf esxi-unlocker-${{ env.RELEASE_VERSION }}.tgz *.*
# ls -al more debug
# release automation.gz
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.ref }}.${{ github.run_number }}
release_name: Release ${{ env.RELEASE_VERSION }}
draft: true
prerelease: true
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./readme.md
asset_name: readme.md
asset_content_type: text/markdown
- name: Upload Release Asset2
id: upload-release-asset2
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./esxi-unlocker-${{ env.RELEASE_VERSION }}.tgz
asset_name: esxi-unlocker-${{ env.RELEASE_VERSION }}.tgz
asset_content_type: application/zip
================================================
FILE: .github/workflows/manual.yml
================================================
# This is a basic workflow to help you get started with Actions
name: Manual CI
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
workflow_dispatch:
inputs:
semver:
description: 'Version'
required: true
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Runs a few command using the runners shell
- name: Run a multi-line script
run: |
# pwd debug
tar -cvzf unlocker.tgz etc
tar -cvzf esxi-unlocker-${{ github.event.inputs.semver }}.tgz *.*
# ls -al more debug
# release automation.gz
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.event.inputs.semver }}
release_name: Release ${{ github.event.inputs.semver }}
# body: |
# Changes in this Release
# - First Change
# - Second Change
draft: true
prerelease: true
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./readme.md
asset_name: readme.md
asset_content_type: text/plain
- name: Upload Release Asset2
id: upload-release-asset2
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./esxi-unlocker-${{ github.event.inputs.semver }}.tgz
asset_name: esxi-unlocker-${{ github.event.inputs.semver }}.tgz
asset_content_type: application/zip
================================================
FILE: .gitignore
================================================
backup/
tools/
.idea/
samples/
tests/
build/
dist/
*.spec
*.pyc
*.tar
*.tgz
*.vmtar
*.vgz
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 David Parsons
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 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.
================================================
FILE: esxi-build.py
================================================
#!/usr/bin/env python
"""
The MIT License (MIT)
Copyright (c) 2014-2018 Dave Parsons & Sam Bingner
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 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.
"""
import datetime
import subprocess
import sys
if sys.version_info < (2, 7):
sys.stderr.write('You need Python 2.7 or later\n')
sys.exit(1)
# TODO: Change for a new release
VERSION = '3.0.2'
FILEVER = '302'
FILENAME = 'esxi-unlocker-302.tgz'
TIMESTAMP = '{:%Y%m%d%H%M.%S}'.format(datetime.datetime.now())
TOUCH = 'touch -t ' + TIMESTAMP
GTARUNLOCKER = '/usr/local/bin/gtar czvf unlocker.tgz etc'
GTARDISTRIB = '/usr/local/bin/gtar czvf ' + FILENAME + \
' unlocker.tgz esxi-install.sh esxi-uninstall.sh esxi-smctest.sh readme.txt'
def main():
# Timestamp files for release
print('\nTimestamping files...')
subprocess.call(TOUCH + ' readme.txt', shell=True)
subprocess.call(TOUCH + ' esxi-install.sh', shell=True)
subprocess.call(TOUCH + ' esxi-uninstall.sh', shell=True)
subprocess.call(TOUCH + ' esxi-smctest.sh', shell=True)
subprocess.call(TOUCH + ' etc', shell=True)
subprocess.call(TOUCH + ' etc/rc.local.d', shell=True)
subprocess.call(TOUCH + ' etc/rc.local.d/unlocker.py', shell=True)
# Build the gzipped tar file unlocker.tgz
print('\nCreating unlocker.tgz...')
subprocess.call(GTARUNLOCKER, shell=True)
subprocess.call(TOUCH + ' unlocker.tgz', shell=True)
# Build the distribution file esxi-unlocker-VER.tgz
print('\nCreating ' + FILENAME + '...')
subprocess.call(GTARDISTRIB, shell=True)
subprocess.call(TOUCH + ' ' + FILENAME, shell=True)
if __name__ == '__main__':
if sys.platform == 'darwin':
print('ESXi-Build for macOS')
main()
else:
print('ESXi-Build only supported on macOS')
================================================
FILE: esxi-install.sh
================================================
#!/bin/sh
set -e
#set -x
echo VMware Unlocker 3.0.2
echo ===============================
echo Copyright: Dave Parsons 2011-18
# Ensure we only use unmodified commands
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
echo Installing unlocker.tgz
BootModuleConfig.sh --verbose --add=unlocker.tgz
echo Success - please now restart the server!
================================================
FILE: esxi-smctest.sh
================================================
#!/bin/sh
grep -il \(c\)AppleComputerInc /bin/vmx*
vim-cmd hostsvc/hosthardware | grep smcPresent | cut -d ',' -f 1 | sed 's/^[ \t]*//'
esxcli system visorfs tardisk list | grep custom.vgz
================================================
FILE: esxi-uninstall.sh
================================================
#!/bin/sh
set -e
#set -x
echo VMware Unlocker 3.0.2
echo ===============================
echo Copyright: Dave Parsons 2011-18
# Ensure we only use unmodified commands
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
echo Uninstalling unlocker.tgz
BootModuleConfig.sh --verbose --remove=unlocker.tgz
echo Success - please now restart the server!
================================================
FILE: etc/rc.local.d/unlocker.py
================================================
#!/usr/bin/env python
"""
The MIT License (MIT)
Copyright (c) 2014-2018 Dave Parsons & Sam Bingner
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 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.
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
"""
import codecs
import os
import shutil
import struct
import subprocess
import sys
if sys.version_info < (2, 7):
sys.stderr.write('You need Python 2.7 or later\n')
sys.exit(1)
def bytetohex(data):
if sys.version_info > (3, 0):
# Python 3 code in this block
return "".join("{:02X} ".format(c) for c in data)
else:
# Python 2 code in this block
return "".join("{:02X} ".format(ord(c)) for c in data)
def joinpath(folder, filename):
return os.path.join(folder, filename)
def printkey(i, offset, smc_key, smc_data):
print(str(i + 1).zfill(3)
+ ' ' + hex(offset)
+ ' ' + smc_key[0][::-1].decode('UTF-8')
+ ' ' + str(smc_key[1]).zfill(2)
+ ' ' + smc_key[2][::-1].replace(b'\x00', b' ').decode('UTF-8')
+ ' ' + '{0:#0{1}x}'.format(smc_key[3], 4)
+ ' ' + hex(smc_key[4])
+ ' ' + bytetohex(smc_data))
def set_bit(value, bit):
return value | (1 << bit)
def clear_bit(value, bit):
return value & ~(1 << bit)
def test_bit(value, bit):
return value & bit
E_CLASS64 = 2
E_SHT_RELA = 4
def patchelf(f, oldoffset, newoffset):
f.seek(0)
magic = f.read(4)
if not magic == b'\x7fELF':
raise Exception('Magic number does not match')
ei_class = struct.unpack('=B', f.read(1))[0]
if ei_class != E_CLASS64:
raise Exception('Not 64bit elf header: ' + ei_class)
f.seek(40)
e_shoff = struct.unpack('=Q', f.read(8))[0]
f.seek(58)
e_shentsize = struct.unpack('=H', f.read(2))[0]
e_shnum = struct.unpack('=H', f.read(2))[0]
e_shstrndx = struct.unpack('=H', f.read(2))[0]
print('e_shoff: 0x{:x} e_shentsize: 0x{:x} e_shnum:0x{:x} e_shstrndx:0x{:x}'.format(e_shoff, e_shentsize,
e_shnum, e_shstrndx))
for i in range(0, e_shnum):
f.seek(e_shoff + i * e_shentsize)
e_sh = struct.unpack('=LLQQQQLLQQ', f.read(e_shentsize))
# The name is not used.
# e_sh_name = e_sh[0]
e_sh_type = e_sh[1]
e_sh_offset = e_sh[4]
e_sh_size = e_sh[5]
e_sh_entsize = e_sh[9]
if e_sh_type == E_SHT_RELA:
e_sh_nument = int(e_sh_size / e_sh_entsize)
# print 'RELA at 0x{:x} with {:d} entries'.format(e_sh_offset, e_sh_nument)
for j in range(0, e_sh_nument):
f.seek(e_sh_offset + e_sh_entsize * j)
rela = struct.unpack('=QQq', f.read(e_sh_entsize))
r_offset = rela[0]
r_info = rela[1]
r_addend = rela[2]
if r_addend == oldoffset:
r_addend = newoffset
f.seek(e_sh_offset + e_sh_entsize * j)
f.write(struct.pack('=QQq', r_offset, r_info, r_addend))
print('Relocation modified at: ' + hex(e_sh_offset + e_sh_entsize * j))
def patchkeys(f, key):
# Setup struct pack string
key_pack = '=4sB4sB6xQ'
# smc_old_memptr = 0
smc_new_memptr = 0
# Do Until OSK1 read
i = 0
while True:
# Read key into struct str and data byte str
offset = key + (i * 72)
f.seek(offset)
smc_key = struct.unpack(key_pack, f.read(24))
smc_data = f.read(smc_key[1])
# Reset pointer to beginning of key entry
f.seek(offset)
if smc_key[0] == b'SKL+':
# Use the +LKS data routine for OSK0/1
smc_new_memptr = smc_key[4]
print('+LKS Key: ')
printkey(i, offset, smc_key, smc_data)
elif smc_key[0] == b'0KSO':
# Write new data routine pointer from +LKS
print('OSK0 Key Before:')
printkey(i, offset, smc_key, smc_data)
# smc_old_memptr = smc_key[4]
f.seek(offset)
f.write(struct.pack(key_pack, smc_key[0], smc_key[1], smc_key[2], smc_key[3], smc_new_memptr))
f.flush()
# Write new data for key
f.seek(offset + 24)
smc_new_data = codecs.encode('bheuneqjbexolgurfrjbeqfthneqrqcy', 'rot_13')
f.write(smc_new_data.encode('UTF-8'))
f.flush()
# Re-read and print key
f.seek(offset)
smc_key = struct.unpack(key_pack, f.read(24))
smc_data = f.read(smc_key[1])
print('OSK0 Key After:')
printkey(i, offset, smc_key, smc_data)
elif smc_key[0] == b'1KSO':
# Write new data routine pointer from +LKS
print('OSK1 Key Before:')
printkey(i, offset, smc_key, smc_data)
smc_old_memptr = smc_key[4]
f.seek(offset)
f.write(struct.pack(key_pack, smc_key[0], smc_key[1], smc_key[2], smc_key[3], smc_new_memptr))
f.flush()
# Write new data for key
f.seek(offset + 24)
smc_new_data = codecs.encode('rnfrqbagfgrny(p)NccyrPbzchgreVap', 'rot_13')
f.write(smc_new_data.encode('UTF-8'))
f.flush()
# Re-read and print key
f.seek(offset)
smc_key = struct.unpack(key_pack, f.read(24))
smc_data = f.read(smc_key[1])
print('OSK1 Key After:')
printkey(i, offset, smc_key, smc_data)
# Finished so get out of loop
break
else:
pass
i += 1
return smc_old_memptr, smc_new_memptr
def patchsmc(name, sharedobj):
with open(name, 'r+b') as f:
smc_old_memptr = 0
smc_new_memptr = 0
# Read file into string variable
vmx = f.read()
print('File: ' + name + '\n')
# 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'
# Setup hex string for #KEY key
key_key = b'\x59\x45\x4B\x23\x04\x32\x33\x69\x75'
# Setup hex string for $Adr key
adr_key = b'\x72\x64\x41\x24\x04\x32\x33\x69\x75'
# Find the vSMC headers
smc_header_v0_offset = vmx.find(smc_header_v0) - 8
smc_header_v1_offset = vmx.find(smc_header_v1) - 8
# Find '#KEY' keys
smc_key0 = vmx.find(key_key)
smc_key1 = vmx.rfind(key_key)
# Find '$Adr' key only V1 table
smc_adr = vmx.find(adr_key)
# Print vSMC0 tables and keys
print('appleSMCTableV0 (smc.version = "0")')
print('appleSMCTableV0 Address : ' + hex(smc_header_v0_offset))
print('appleSMCTableV0 Private Key #: 0xF2/242')
print('appleSMCTableV0 Public Key #: 0xF0/240')
if (smc_adr - smc_key0) != 72:
print('appleSMCTableV0 Table : ' + hex(smc_key0))
smc_old_memptr, smc_new_memptr = patchkeys(f, smc_key0)
elif (smc_adr - smc_key1) != 72:
print('appleSMCTableV0 Table : ' + hex(smc_key1))
smc_old_memptr, smc_new_memptr = patchkeys(f, smc_key1)
print()
# Print vSMC1 tables and keys
print('appleSMCTableV1 (smc.version = "1")')
print('appleSMCTableV1 Address : ' + hex(smc_header_v1_offset))
print('appleSMCTableV1 Private Key #: 0x01B4/436')
print('appleSMCTableV1 Public Key #: 0x01B0/432')
if (smc_adr - smc_key0) == 72:
print('appleSMCTableV1 Table : ' + hex(smc_key0))
smc_old_memptr, smc_new_memptr = patchkeys(f, smc_key0)
elif (smc_adr - smc_key1) == 72:
print('appleSMCTableV1 Table : ' + hex(smc_key1))
smc_old_memptr, smc_new_memptr = patchkeys(f, smc_key1)
print()
# Find matching RELA record in .rela.dyn in ESXi ELF files
# This is temporary code until proper ELF parsing written
if sharedobj:
print('Modifying RELA records from: ' + hex(smc_old_memptr) + ' to ' + hex(smc_new_memptr))
patchelf(f, smc_old_memptr, smc_new_memptr)
# Tidy up
f.flush()
f.close()
def patchvmkctl(name):
# Patch file
print('smcPresent Patching: ' + name)
f = open(name, 'r+b')
# Read file into string variable
vmkctl = f.read()
applesmc = vmkctl.find(b'applesmc')
f.seek(applesmc)
f.write(b'vmkernel')
# Tidy up
f.flush()
f.close()
print('smcPresent Patched: ' + name)
def main():
# Stop the hostd service
subprocess.call('/etc/init.d/hostd stop', shell=True)
# Current folder
currdir = os.getcwd()
# Source files
srcvmx = '/bin/vmx'
srclib32 = '/lib/libvmkctl.so'
srclib64 = '/lib64/libvmkctl.so'
# Destination files currently tmp but may use scratch
basefolder = '/tmp/'
destfolder = joinpath(basefolder, 'unlocker')
destvmx = joinpath(destfolder, 'bin/vmx')
destlib32 = joinpath(destfolder, 'lib/libvmkctl.so')
destlib64 = joinpath(destfolder, 'lib64/libvmkctl.so')
# Remove files & folder if they exist
if os.path.isdir(destfolder):
shutil.rmtree(destfolder, True)
# Create the base folder
os.makedirs(destfolder)
os.chdir(destfolder)
# Patch the vmx executable
os.makedirs(joinpath(destfolder, 'bin'))
shutil.copy2(srcvmx, destvmx)
patchsmc(destvmx, True)
# Patch 32-bit libvmkctl to return Apple SMC present
os.makedirs(joinpath(destfolder, 'lib'))
if os.path.isfile(srclib32):
shutil.copy2(srclib32, destlib32)
patchvmkctl(destlib32)
# Patch 64-bit libvmkctl to return Apple SMC present
if os.path.isfile(srclib64):
os.makedirs(joinpath(destfolder, 'lib64'))
shutil.copy2(srclib64, destlib64)
patchvmkctl(destlib64)
# Build the gzipped tar file custom.tgz
print('\nCreating custom.tgz...')
subprocess.call('/bin/tar czvf custom.tgz bin lib lib64', shell=True)
# Build the vmtar file custom.vmtar
print('\nCreating custom.vmtar...')
subprocess.call('/bin/vmtar -v -c custom.tgz -o custom.vmtar', shell=True)
# Build the gzipped vmtar file custom.vgz
print('\nCreating custom.vgz...')
subprocess.call('/bin/gzip < custom.vmtar > custom.vgz', shell=True)
subprocess.call('/bin/vmtar -v -t < custom.vgz', shell=True)
# Load the tardisk
subprocess.call('vmkramdisk custom.vgz', shell=True)
# Return to script folder
os.chdir(currdir)
# Start the hostd service
subprocess.call('/etc/init.d/hostd start', shell=True)
if __name__ == '__main__':
# Check boot options specified
bootoptions = subprocess.check_output(['/bin/bootOption', '-a'])
if bootoptions.find(b'nounlocker') == -1:
# Run unlocker code
main()
else:
# Exit if "nounlocker" boot option specified
print('Unlocker disabled by boot option')
================================================
FILE: readme.md
================================================
# macOS Unlocker V3.0.2 for VMware ESXi
## 1. Introduction
Unlocker 3 for ESXi is designed for VMware ESXi 6.5, 6.7 and 7.0
The patch code carries out the following modifications dependent on the product
being patched:
* Fix vmware-vmx to allow macOS to boot
* Fix libvmkctl to allow vSphere to control the guest
The code is written in Python as it makes the Unlocker easier to run and
maintain on ESXi.
>
> *IMPORTANT:* |
>
> Always uninstall the previous version of the Unlocker before using a new
> version. Failure to do this could render VMware unusable.
## 2. Installation
Copy the latest release file to the ESXi host datastore using scp or some other
data transfer system. If you want to use the source version (i.e. from GIT) see
"5. Building" fist.
Decompress the file from the ESXi console or via SSH:
tar xzvf esxi-unlocker-xxx.tgz
(xxx - will be the version number, for example, 3.0.0)
Run the command from the terminal:
./esxi-install.sh
Finally reboot the server.
## 3. Uninstallation
Open the ESXi console or login via SSH and change to the folder where the files were extracted.
Run the command from the terminal:
./esxi-uninstall.sh
Finally reboot the server.
## 4. Notes
A. There is a command added called esxi-smctest.sh which can show if the patch is successful. It must be run from a
terminal or SSH session. The output should be:
/bin/vmx
smcPresent = true
custom.vgz false 32486592 B
Note: The uncompressed size reported for custom.vgz will vary depending on the ESXi version.
B. The unlocker can be temporarily disabled during boot by editing the boot options and adding "nounlocker".
## 5. Building
If you want to use a version which is not available as a distribution (e.g. the code from "master" branch)
you need to first build the package. You can build locally on a Mac or via a github workflow.
### Local Build
Checkout the repository:
git clone https://github.com/shanyungyang/esxi-unlocker.git
(if you don't have git installed you can download ZIP archive from GitHub instead)
Enter the directory and build:
cd esxi-unlocker
./esxi-build.py
If everything went correctly the ouput should be:
ESXi-Build for macOS
Timestamping files...
Creating unlocker.tgz...
etc/
etc/rc.local.d/
etc/rc.local.d/unlocker.py
Creating esxi-unlocker-301.tgz...
unlocker.tgz
esxi-install.sh
esxi-uninstall.sh
esxi-smctest.sh
readme.txt
The package you need to copy in the example above is esxi-unlocker-301.tgz (NOT unlocker.tgz!).
### Github Build
#### Triggered
If you add a tag to any commit in the semver format `*.*.*` a triggered build will run and create a release.
#### Manual
Fork the repository, click on actions, select manual CI and then run.
This will build a new draft release for you which you can upload to your esxi using curl or wget.
If you add a tag in the format `*.*.*` a triggered build will run without needing
## 6. Thanks
Thanks to Zenith432 for originally building the C++ unlocker and Mac Son of Knife
(MSoK) for all the testing and support.
Thanks also to Sam B for finding the solution for ESXi 6 and helping me with
debugging expertise. Sam also wrote the code for patching ESXi ELF files and
modified the unlocker code to run on Python 3 in the ESXi 6.5 environment.
# History
26/09/18 3.0.0 - First release
01/05/20 3.0.1 - Fix for ESXi 7.0
10/18/20 3.0.2 - Fix for ESXi 7.0 U1 (7.0.1)
10/29/20 3.0.3 - Release process automated
(c) 2011-2018 Dave Parsons
gitextract_zmf1ry0m/ ├── .gitattributes ├── .github/ │ └── workflows/ │ ├── main.yml │ └── manual.yml ├── .gitignore ├── LICENSE ├── esxi-build.py ├── esxi-install.sh ├── esxi-smctest.sh ├── esxi-uninstall.sh ├── etc/ │ └── rc.local.d/ │ └── unlocker.py └── readme.md
SYMBOL INDEX (12 symbols across 2 files) FILE: esxi-build.py function main (line 47) | def main(): FILE: etc/rc.local.d/unlocker.py function bytetohex (line 56) | def bytetohex(data): function joinpath (line 65) | def joinpath(folder, filename): function printkey (line 69) | def printkey(i, offset, smc_key, smc_data): function set_bit (line 80) | def set_bit(value, bit): function clear_bit (line 84) | def clear_bit(value, bit): function test_bit (line 88) | def test_bit(value, bit): function patchelf (line 96) | def patchelf(f, oldoffset, newoffset): function patchkeys (line 141) | def patchkeys(f, key): function patchsmc (line 220) | def patchsmc(name, sharedobj): function patchvmkctl (line 294) | def patchvmkctl(name): function main (line 311) | def main():
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (28K chars).
[
{
"path": ".gitattributes",
"chars": 375,
"preview": "# Recommedations from GitHub help pages\n# Set the default behavior, in case people don't have core.autocrlf set.\n* text="
},
{
"path": ".github/workflows/main.yml",
"chars": 2448,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: CI\n\n# Controls when the action will run. Triggers"
},
{
"path": ".github/workflows/manual.yml",
"chars": 2435,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: Manual CI\n\n# Controls when the action will run. T"
},
{
"path": ".gitignore",
"chars": 90,
"preview": "backup/\ntools/\n.idea/\nsamples/\ntests/\nbuild/\ndist/\n*.spec\n*.pyc\n*.tar\n*.tgz\n*.vmtar\n*.vgz\n"
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2018 David Parsons\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "esxi-build.py",
"chars": 2753,
"preview": "#!/usr/bin/env python\n\"\"\"\nThe MIT License (MIT)\n\nCopyright (c) 2014-2018 Dave Parsons & Sam Bingner\n\nPermission is hereb"
},
{
"path": "esxi-install.sh",
"chars": 336,
"preview": "#!/bin/sh\nset -e\n#set -x\n\necho VMware Unlocker 3.0.2\necho ===============================\necho Copyright: Dave Parsons 2"
},
{
"path": "esxi-smctest.sh",
"chars": 189,
"preview": "#!/bin/sh\ngrep -il \\(c\\)AppleComputerInc /bin/vmx*\nvim-cmd hostsvc/hosthardware | grep smcPresent | cut -d ',' -f 1 | se"
},
{
"path": "esxi-uninstall.sh",
"chars": 341,
"preview": "#!/bin/sh\nset -e\n#set -x\n\necho VMware Unlocker 3.0.2\necho ===============================\necho Copyright: Dave Parsons 2"
},
{
"path": "etc/rc.local.d/unlocker.py",
"chars": 12757,
"preview": "#!/usr/bin/env python\n\"\"\"\nThe MIT License (MIT)\n\nCopyright (c) 2014-2018 Dave Parsons & Sam Bingner\n\nPermission is hereb"
},
{
"path": "readme.md",
"chars": 3760,
"preview": "# macOS Unlocker V3.0.2 for VMware ESXi\n\n\n## 1. Introduction\n\n\nUnlocker 3 for ESXi is designed for VMware ESXi 6.5, 6.7 "
}
]
About this extraction
This page contains the full source code of the shanyungyang/esxi-unlocker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (25.9 KB), approximately 7.5k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.