diff --git a/readme-bbcode.txt b/readme-bbcode.txt new file mode 100644 index 0000000..f3a3d87 --- /dev/null +++ b/readme-bbcode.txt @@ -0,0 +1,181 @@ +[u][b]Mac OS X Unlocker for VMware V2.0[/b][/u] + + +[u]1. Introduction[/u] + +Unlocker 2 is designed for Workstation 11, Player 7, ESXi 6 and Fusion 7. + +If you are using an earlier product please continue using Unlocker 1 + +Version 2 has been tested against: +[LIST] +[*]Workstation 11/12 on Windows and Linux[/*] +[*]Player 7 & Workstation Player 12 on Windows and Linux[/*] +[*]Fusion 7/8 on Mavericks and Yosemite[/*] +[*]ESXi 6.0[/*] +[/LIST] +The patch code carries out the following modifications dependent on the product +being patched: +[LIST] +[*]Fix vmware-vmx and derivatives to allow Mac OS X to boot[/*] +[*]Fix vmwarebase .dll or .so to allow Apple to be selected during VM creation[/*] +[*]Fix libvmkctl.so on ESXi 6 to allow use with vCenter[/*] +[*]A copy of the latest VMware Tools for OS X is included[/*] +[/LIST] + +Note that not all products recognise the darwin.iso via install tools menu item. +You will have to manually mount the darwin.iso for example on Workstation and Player. + +The vmwarebase code does not need to be patched on OS X or ESXi so you will see a +message on those systems telling you that it will not be patched. + +In all cases make sure VMware is not running, and any background guests have +been shutdown. + +The code is now Python as it makes the Unlocker easier to run and maintain on ESXi. +There are some challenges to write the code as ESXi has a subset of Python 2.7 which +constrains some modules that can be used. + +[u]2. Prerequisites[/u] + +The code requires Python 2.7 to work. Most Linux distros, ESXi and OS X ship with a compatible +Python interpreter and should work without requiring any additional software. + +Windows has a packaged version of the Python script using PyInstaller, and so does not +require Python to be installed. + +[u]3. Limitations[/u] + +If you are using VMware Player or Workstation on Windows you may get a core dump. + +Latest Linux and ESXi products are OK and do not show this problem. + +[color=#ff0000][u][b] IMPORTANT:[/b][/u] + + If you create a new VM using version 11 hardware VMware will stop and  + create a core dump.There are two options to work around this issue: + + 1. Change the VM to be HW 10 - this does not affect performance. + 2. Edit the VMX file and add: [/color] +  +[code=auto:0] smc.version = "0" |[/code] +  + +To remove the check for server versions for OS X Leopard and Snow Leopard +(10.5 and 10.6) you must use a replacement EFI firwmare module from the firmware +folder. + +If you are using a 32-bit installation of OS X: + +1. Copy efi32-srvr.rom to guest folder. +2. Edit the vmx file and add: +  +[code=auto:0]efi32.filename = "efi32-srvr.rom"[/code] +  +If you are using a 64-bit installation of OS X: + +1. Copy efi64-srvr.rom to guest folder. +2. Edit the vmx file and add: +  +[code=auto:0]efi64.filename = "efi64-srvr.rom"[/code] +  +[u]4. Windows[/u] + +On Windows you will need to either run cmd.exe as Administrator or using +Explorer right click on the command file and select "Run as administrator". + +win-install.cmd - patches VMware +win-uninstall.cmd - restores VMware + +[u]5. Linux[/u] + +On Linux you will need to be either root or use sudo to run the scripts. + +You may need to ensure the Linux scripts have execute permissions +by running chmod +x against the 2 files. + +lnx-install.sh - patches VMware +lnx-uninstall.sh - restores VMware + +[u]6. Mac OS X[/u] + +On Mac OS X you will need to be either root or use sudo to run the scripts. +This is really only needed if you want to use client versions of Mac OS X. + +You may need to ensure the OS X scripts have execute permissions +by running chmod +x against the 2 files. + +osx-install.sh - patches VMware +osx-uninstall.sh - restores VMware + +[u]7. ESXi[/u] + +You will need to transfer the zip file to the ESXi host either using vSphere client or SCP. + +Once uploaded you will need to either use the ESXi support console or use SSH to +run the commands. Use the unzip command to extract the files. + +[color=#ff0000][b]<<< WARNING: use a datastore volume to run the scripts >>>[/b][/color] + +Please note that you will need to reboot the host for the patches to become active. +The patcher is embbedded in a shell script local.sh which is run at boot from /etc/rc.local.d. + +You may need to ensure the ESXi scripts have execute permissions +by running chmod +x against the 2 files. + +esxi-install.sh - patches VMware +esxi-uninstall.sh - restores VMware + +Note: +1. Any changes you have made to local.sh will be lost. If you have made changes to +that file, you will need to merge them into the supplied local.sh file. +2. The unlocker runs at boot time to patch the relevant files and it now survives +an upgrade or patch to ESXi as local.sh is part of the persisted local state. + +[u]8. Thanks[/u] + +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. + + +[u]History[/u] + +12/12/14 2.0.0 +[LIST] +[*]First release[/*] +[/LIST] +13/13/14 2.0.1 +[LIST] +[*]Removed need for Python for Windows[/*] +[/LIST] +13/13/14 2.0.2 +[LIST] +[*]darwin.iso was missing from zip file[/*] +[/LIST] +02/01/15 2.0.3 +[LIST] +[*]Added EFI firmware files to remove Server check[/*] +[*]Refactored Python code[/*] +[/LIST] +07/01/15 2.0.4 +[LIST] +[*]Added View USB Service to Windows batch files[/*] +[*]Fixed broken GOS Table patching on Linux[/*] +[/LIST] +18/06/15 2.0.5 +[LIST] +[*]ESXi 6 working[/*] +[*]Latest tools from Fusion 7.1.2[/*] +[/LIST] +20/06/15 2.0.6 +[LIST] +[*]ESXi 6 patch for smcPresent vCenter compatibility[/*] +[/LIST] +16/09/15 2.0.7 +[LIST] +[*]Workstation 12 on Linux fixes[/*] +[/LIST] +(c) 2011-2015 Dave Parsons \ No newline at end of file diff --git a/unlocker-asmpatch.diff b/unlocker-asmpatch.diff new file mode 100644 index 0000000..b568c3b --- /dev/null +++ b/unlocker-asmpatch.diff @@ -0,0 +1,276 @@ +--- unlocker.py 2015-06-19 15:45:49.557221936 -1000 ++++ unlocker-sam.py 2015-06-19 15:46:27.238391426 -1000 +@@ -75,51 +75,208 @@ + + ' ' + hex(smc_key[4]) \ + + ' ' + bytetohex(smc_data) + +-E_CLASS64 = 2; +-E_SHT_RELA = 4; ++E_CLASS32 = 1 ++E_CLASS64 = 2 ++E_SHT_STRTAB = 3 ++E_SHT_RELA = 4 ++E_SHT_DYNSYM = 11 ++ ++# Indexes for e_ident ++EI_MAGIC = 0 ++EI_CLASS = 1 ++EI_DATA = 2 ++EI_VERSION = 3 ++EI_OSABI = 4 ++EI_ABIVERSION = 5 ++EI_PAD = 6 ++ ++# Data Types ++ELFDATA2LSB = 1 ++ELFDATA2MSB = 2 ++ ++EI_PACK = '!LBBBBB7B' ++ ++ehPack = '' ++shPack = '' ++relaPack = '' ++dynSymPack = '' ++ ++def readELF_sh(f, offset, shPack, e_shstr_off = None): ++ f.seek(offset) ++ e_sh_raw = struct.unpack(shPack, f.read(struct.calcsize(shPack))) ++ e_sh_entry = dict( ++ name = e_sh_raw[0], ++ type = e_sh_raw[1], ++ flags = e_sh_raw[2], ++ addr = e_sh_raw[3], ++ offset = e_sh_raw[4], ++ size = e_sh_raw[5], ++ link = e_sh_raw[6], ++ info = e_sh_raw[7], ++ addralign = e_sh_raw[8], ++ entsize = e_sh_raw[9] ++ ) ++ if e_sh_entry['entsize'] != 0: ++ e_sh_entry['nument'] = e_sh_entry['size'] / e_sh_entry['entsize'] ++ else: ++ e_sh_entry['nument'] = 0 ++ ++ ++ if e_shstr_off is not None: ++ f.seek(e_shstr_off + e_sh_entry['name']) ++ e_sh_entry['nameText'] = f.read(0x40).split(b'\x00')[0] ++ else: ++ e_sh_entry['nameText'] = '' ++ ++ return e_sh_entry ++ ++def readELF_sym(f, offset, dynSymPack): ++ f.seek(offset) ++ size = struct.calcsize(dynSymPack) ++ sym_raw = struct.unpack(dynSymPack, f.read(size)) ++ if (size == 16): ++ sym = dict( ++ name = sym_raw[0], ++ value = sym_raw[1], ++ size = sym_raw[2], ++ info = sym_raw[3], ++ other = sym_raw[4], ++ shndx = sym_raw[5] ++ ) ++ elif (size == 24): ++ sym = dict( ++ name = sym_raw[0], ++ info = sym_raw[1], ++ other = sym_raw[2], ++ shndx = sym_raw[3], ++ value = sym_raw[4], ++ size = sym_raw[5] ++ ) ++ else: ++ raise Exception('Unknown size: {:d}'.format(size)) ++ ++ return sym ++ ++def findSym(f, dynSymPack, dynsym_sh, dynstr_sh, symbol): ++ f.seek(dynstr_sh['offset']) ++ index = f.read(dynstr_sh['size']).find(symbol) ++ if index >= 0: ++ for i in range(0, dynsym_sh['nument']): ++ sym = readELF_sym(f, dynsym_sh['offset'] + dynsym_sh['entsize'] * i, dynSymPack) ++ if sym['name'] == index: ++ sym['nameText'] = symbol ++ print 'Found {:s} at index {:d}'.format(symbol, i) ++ return sym ++ return -1 ++ ++def readELF_hdr(f): ++ global ehPack, shPack, relaPack, dynSymPack + +-def patchELF(f, oldOffset, newOffset): + f.seek(0) +- magic = f.read(4) +- if not magic == b'\x7fELF': ++ e_ident = struct.unpack(EI_PACK, f.read(16)) ++ if not e_ident[EI_MAGIC] == 0x7F454C46: + 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)) +- 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 = 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) ++ if e_ident[EI_DATA] == ELFDATA2LSB: ++ fileEncoding = '<' ++ elif e_ident[EI_DATA] == ELFDATA2MSB: ++ fileEncoding = '>' ++ else: ++ raise Exception('Invalid data encoding: {:d}'.format(e_ident[EI_DATA])) ++ ++ ++ if e_ident[EI_CLASS] == E_CLASS64: ++ ehPack = fileEncoding + 'HHLQQQLHHHHHH' ++ shPack = fileEncoding + 'LLQQQQLLQQ' ++ relaPack = fileEncoding + 'QQq' ++ dynSymPack = fileEncoding + 'LBBHQQ' ++ elif e_ident[EI_CLASS] == E_CLASS32: ++ ehPack = fileEncoding + 'HHLLLLLHHHHHH' ++ shPack = fileEncoding + 'LLLLLLLLLL' ++ relaPack = fileEncoding + 'LLl' ++ dynSymPack = fileEncoding + 'LLLBBH' ++ else: ++ raise Exception('Not 32 or 64bit elf header: {:d}'.format(e_ident[EI_CLASS])) ++ ++ e_hdr_raw = struct.unpack(ehPack, f.read(struct.calcsize(ehPack))) ++ e_hdr = dict( ++ ident = e_ident, ++ type = e_hdr_raw[0], ++ machine = e_hdr_raw[1], ++ version = e_hdr_raw[2], ++ entry = e_hdr_raw[3], ++ phoff = e_hdr_raw[4], ++ shoff = e_hdr_raw[5], ++ flags = e_hdr_raw[6], ++ ehsize = e_hdr_raw[7], ++ phentsize = e_hdr_raw[8], ++ phnum = e_hdr_raw[9], ++ shentsize = e_hdr_raw[10], ++ shnum = e_hdr_raw[11], ++ shstrndx = e_hdr_raw[12] ++ ) ++ ++ # print 'e_shoff: 0x{:x} e_shentsize: 0x{:x} e_shnum:0x{:x} e_shstrndx:0x{:x}'.format(e_hdr['shoff'], e_hdr['shentsize'], e_hdr['shnum'], e_hdr['shstrndx']) ++ ++ return e_hdr + ++def patchELF_rela(f, oldOffset, newOffset): ++ e_hdr = readELF_hdr(f) ++ ++ e_shstr_off = readELF_sh(f, e_hdr['shoff'] + e_hdr['shstrndx'] * e_hdr['shentsize'], shPack)['offset'] ++ ++ e_shs = [] ++ ++ for i in range(0, e_hdr['shnum']): ++ e_sh = readELF_sh(f, e_hdr['shoff'] + i * e_hdr['shentsize'], shPack, e_shstr_off) ++ e_shs.append(e_sh) ++ ++ #print '{:s} at 0x{:x} with {:d} entries'.format(e_sh['nameText'], e_sh['offset'], e_sh['nument']) ++ ++ if e_sh['type'] == E_SHT_RELA: ++ for j in range(0, e_sh['nument']): ++ f.seek(e_sh['offset'] + e_sh['entsize'] * j) ++ rela_raw = struct.unpack(relaPack, f.read(struct.calcsize(relaPack))) ++ rela = dict(offset = rela_raw[0], info = rela_raw[1], addend = rela_raw[2]) ++ if rela['addend'] == oldOffset: ++ rela['addend'] = newOffset ++ f.seek(e_sh['offset'] + e_sh['entsize'] * j) ++ f.write(struct.pack(relaPack, rela['offset'], rela['info'], rela['addend'])) ++ print 'Relocation modified at: ' + hex(e_sh['offset'] + e_sh['entsize'] * j) + ' 0x{:x}'.format(rela['offset']) ++ ++def patchELF_dynsym(f, symbol, newASM): ++ e_hdr = readELF_hdr(f) ++ ++ e_shstr_off = readELF_sh(f, e_hdr['shoff'] + e_hdr['shstrndx'] * e_hdr['shentsize'], shPack)['offset'] ++ e_dynsym_idx = -1; ++ e_dynstr_idx = -1; ++ ++ e_shs = [] ++ ++ for i in range(0, e_hdr['shnum']): ++ e_sh = readELF_sh(f, e_hdr['shoff'] + i * e_hdr['shentsize'], shPack, e_shstr_off) ++ e_shs.append(e_sh) ++ ++ #print '{:s} at 0x{:x} with {:d} entries'.format(e_sh['nameText'], e_sh['offset'], e_sh['nument']) ++ ++ if e_sh['type'] == E_SHT_DYNSYM: ++ if e_sh['nameText'] == '.dynsym': ++ e_dynsym_idx = i; ++ if e_dynsym_idx > 0 and e_dynstr_idx > 0: ++ sym = findSym(f, dynSymPack, e_shs[e_dynsym_idx], e_shs[e_dynstr_idx], symbol) ++ ++ elif e_sh['type'] == E_SHT_STRTAB: ++ if e_sh['nameText'] == '.dynstr': ++ e_dynstr_idx = i; ++ if e_dynsym_idx > 0 and e_dynstr_idx > 0: ++ sym = findSym(f, dynSymPack, e_shs[e_dynsym_idx], e_shs[e_dynstr_idx], symbol) ++ ++ if sym != -1: ++ print 'Patching SMC for symbol {:s} at 0x{:x}'.format(sym['nameText'], sym['value']) ++ f.seek(sym['value']) ++ f.write(newASM) ++ else: ++ print 'Unable to patch ' + symbol + + def patchkeys(f, vmx, key, osname): + # Setup struct pack string +@@ -258,10 +415,9 @@ + print + + # Find matching RELA record in .rela.dyn in ESXi ELF files +- # This is temporary code until proper ELF parsing written + if osname == 'vmkernel': + print 'Modifying RELA records from: ' + hex(smc_old_memptr) + ' to ' + hex(smc_new_memptr) +- patchELF(f, smc_old_memptr, smc_new_memptr) ++ patchELF_rela(f, smc_old_memptr, smc_new_memptr) + + # Tidy up + f.flush() +@@ -314,11 +470,7 @@ + print 'smcPresent Patching: ' + name + f = open(name, 'r+b') + +- # Read file into string variable +- vmkctl = f.read() +- applesmc = vmkctl.find('applesmc') +- f.seek(applesmc) +- f.write('vmkernel') ++ patchELF_dynsym(f, '_ZN6VmkCtl8Hardware16HardwareInfoImpl12IsSmcPresentEv', '\xb8\x01\x00\x00\x00\xc3') + + # Tidy up + f.flush()