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