ELF Patching code

This commit is contained in:
Dave Parsons 2015-09-16 17:19:01 +01:00
parent 3c80ee7a67
commit 2d86d7179f
2 changed files with 457 additions and 0 deletions

181
readme-bbcode.txt Normal file
View File

@ -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

276
unlocker-asmpatch.diff Normal file
View File

@ -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()