Skip to content

Commit

Permalink
Fix nested dialogs cause prefs to crash (Bamboo)
Browse files Browse the repository at this point in the history
  • Loading branch information
thenickdude committed Nov 24, 2022
1 parent 5a0e133 commit 46a12ff
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
src/*/*.patched
src/*/*.patched.*
src/*/*.original
src/*/*.dmg
src/*/*.pkg
src/*/*.bin
src/6.3.17-5/Wacom Desktop Center.app
build/
package/
.venv/
14 changes: 14 additions & 0 deletions src/5.3.7-6/PenTablet.prefpane.beginDialog.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ORG 0x0004ef56

BITS 64
DEFAULT REL

; Patch the call to NSApp->mainWindow to call our stub instead, so we can save the value
; between calls and so avoid the Catalina bug where preference panes see mainWindow
; change to point to the last-opened page (when it should always stay pointed at the
; main window!)
OWacomWindowController_beginDialog:
CALL 0x00339000

; NOP out the following stuff we don't need
TIMES 14-($-$$) DB 0x90
14 changes: 14 additions & 0 deletions src/5.3.7-6/PenTablet.prefpane.getCurrentController.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ORG 0x0004f44c

BITS 64
DEFAULT REL

; Patch the call to NSApp->mainWindow to call our stub instead, so we can save the value
; between calls and so avoid the Catalina bug where preference panes see mainWindow
; change to point to the last-opened page (when it should always stay pointed at the
; main window!)
OWacomWindowController_getCurrentController:
CALL 0x00339000

; NOP out the following stuff we don't need
TIMES 11-($-$$) DB 0x90
42 changes: 42 additions & 0 deletions src/5.3.7-6/PenTablet.prefpane.newcode.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
%define _objc_retainAutoreleasedReturnValue 0x0021b14a

%define got_objc_msgSend 0x002fa810
%define got_objc_release 0x002fa820
%define got_objc_retain 0x002fa828

BITS 64
DEFAULT REL

ORG 0x00339000

_NSApp_mainWindow_stub:
; NSApp is in RDI and mainWindow string in RSI

PUSH RBP ; We need to push 8 bytes to the stack to 16-byte align it anyway
MOV RBP, RSP

; Did we already fetch mainWindow?
MOV RAX, [data_segment]
TEST RAX, RAX

JNZ .leave ; Return it

CALL [got_objc_msgSend]

MOV RDI, RAX
CALL _objc_retainAutoreleasedReturnValue

MOV [data_segment], RAX ; Remember mainWindow for next time

.leave:
; Since our caller will call _objc_release on the mainWindow, but we
; want to keep it around, increase the refcount
MOV RDI, RAX
CALL [got_objc_retain]

POP RBP
RET

TIMES 16384-($-$$) DB 0

data_segment:
Binary file added src/5.3.7-6/PenTablet.prefpane.newdata.bin
Binary file not shown.
Binary file removed src/5.3.7-6/PenTablet.prefpane.patch
Binary file not shown.
27 changes: 25 additions & 2 deletions src/Makefile-bamboo.mk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ EXTRACTED_DRIVERS_5_3_7_6= \
src/5.3.7-6/renumtablets \
src/5.3.7-6/PackageInfo.original \
src/5.3.7-6/Distribution.original \
src/5.3.7-6/uninstall.pl.original
src/5.3.7-6/uninstall.pl.original \
src/5.3.7-6/PenTablet.prefpane.original

PATCHED_DRIVERS_5_3_7_6= \
src/5.3.7-6/PenTabletDriver.patched \
Expand All @@ -15,7 +16,8 @@ PATCHED_DRIVERS_5_3_7_6= \
src/5.3.7-6/postinstall.patched \
src/5.3.7-6/PackageInfo.patched \
src/5.3.7-6/Distribution.patched \
src/5.3.7-6/uninstall.pl.patched
src/5.3.7-6/uninstall.pl.patched \
src/5.3.7-6/PenTablet.prefpane.patched

EXTRACTED_DRIVERS+= $(EXTRACTED_DRIVERS_5_3_7_6)

Expand All @@ -25,6 +27,7 @@ SIGN_ME_5_3_7_6= \
package/content.pkg/Payload/Library/Application\ Support/Tablet/PenTabletDriver.app/Contents/Resources/TabletDriver.app \
package/content.pkg/Payload/Library/Application\ Support/Tablet/ConsumerTouchDriver.app \
package/content.pkg/Payload/Library/Application\ Support/Tablet/PenTabletDriver.app \
package/content.pkg/Payload/Library/PreferencePanes/PenTablet.prefpane/Contents/MacOS/PenTablet \
package/content.pkg/Payload/Library/PreferencePanes/PenTablet.prefpane \
package/content.pkg/Payload/Library/Frameworks/WacomMultiTouch.framework/Versions/A/WacomMultiTouch \
package/content.pkg/Payload/Library/PrivilegedHelperTools/com.wacom.TabletHelper.app/Contents/MacOS/com.wacom.TabletHelper \
Expand All @@ -48,6 +51,9 @@ Install\ Wacom\ Tablet-5.3.7-6-patched-unsigned.pkg : src/5.3.7-6/Install\ Wacom
cp src/5.3.7-6/PenTabletDriver.patched package/content.pkg/Payload/Library/Application\ Support/Tablet/PenTabletDriver.app/Contents/MacOS/PenTabletDriver
cp src/5.3.7-6/ConsumerTouchDriver.patched package/content.pkg/Payload/Library/Application\ Support/Tablet/PenTabletDriver.app/Contents/Resources/ConsumerTouchDriver.app/Contents/MacOS/ConsumerTouchDriver

# Add patched prefpane
cp src/5.3.7-6/PenTablet.prefpane.patched package/content.pkg/Payload/Library/PreferencePanes/PenTablet.prefpane/Contents/MacOS/PenTablet

# Tool for clearing leftover permissions from previous driver:
cp src/5.3.7-6/postinstall.patched package/content.pkg/Scripts/postinstall
cp src/common-5/clearpermissions package/content.pkg/Scripts/
Expand Down Expand Up @@ -105,6 +111,22 @@ endif
# Repack installer
pkgutil --flatten package "$@"

src/5.3.7-6/PenTablet.prefpane.patched : src/5.3.7-6/PenTablet.prefpane.original src/5.3.7-6/PenTablet.prefpane.newcode.bin src/5.3.7-6/PenTablet.prefpane.newdata.bin src/5.3.7-6/PenTablet.prefpane.beginDialog.bin src/5.3.7-6/PenTablet.prefpane.getCurrentController.bin .venv/
./.venv/bin/pip3 install -q -r tools/extend-mach-o/requirements.txt
# Dump me with 'objdump -b binary -m i386:x86-64:intel -D --adjust-vma=0x00339000 PenTablet.prefpane.newcode.bin'
./.venv/bin/python3 tools/extend-mach-o/append-section.py src/5.3.7-6/PenTablet.prefpane.original $@.1 __MONKEYCODE __monkeycode src/5.3.7-6/PenTablet.prefpane.newcode.bin 5
./.venv/bin/python3 tools/extend-mach-o/append-section.py $@.1 $@ __MONKEYDATA __monkeydata src/5.3.7-6/PenTablet.prefpane.newdata.bin 3
# Patch calls to NSApp::mainWindow:
dd if=src/5.3.7-6/PenTablet.prefpane.beginDialog.bin of=$@ bs=1 seek=$$((0x0004ef56)) conv=notrunc
dd if=src/5.3.7-6/PenTablet.prefpane.getCurrentController.bin of=$@ bs=1 seek=$$((0x0004f44c)) conv=notrunc

.venv/ :
python3 -m venv .venv
./.venv/pip3 install

%.bin : %.asm
yasm -f bin -o $@ $<

ifdef PACKAGE_SIGNING_IDENTITY
Install\ Wacom\ Tablet-5.3.7-6-patched.pkg : Install\ Wacom\ Tablet-5.3.7-6-patched-unsigned.pkg
productsign --sign "$(PACKAGE_SIGNING_IDENTITY)" Install\ Wacom\ Tablet-5.3.7-6-patched-unsigned.pkg Install\ Wacom\ Tablet-5.3.7-6-patched.pkg
Expand Down Expand Up @@ -132,6 +154,7 @@ $(EXTRACTED_DRIVERS_5_3_7_6) : src/5.3.7-6/Install\ Wacom\ Tablet.pkg
cp package/content.pkg/Scripts/postinstall src/5.3.7-6/postinstall.original
cp package/content.pkg/Scripts/renumtablets src/5.3.7-6/renumtablets
cp package/content.pkg/Payload/Applications/Pen\ Tablet.localized/Pen\ Tablet\ Utility.app/Contents/Resources/uninstall.pl src/5.3.7-6/uninstall.pl.original
cp package/content.pkg/Payload/Library/PreferencePanes/PenTablet.prefpane/Contents/MacOS/PenTablet src/5.3.7-6/PenTablet.prefpane.original
cp package/Distribution src/5.3.7-6/Distribution.original
cp package/content.pkg/PackageInfo src/5.3.7-6/PackageInfo.original

Expand Down
196 changes: 196 additions & 0 deletions tools/extend-mach-o/append-section.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#!/usr/bin/env python3

# Original code by, and Copyright, Alexander O'Mara:
#
# https://alexomara.com/blog/adding-a-segment-to-an-existing-macos-mach-o-binary/

import io
import os
import sys
import contextlib
from macholib.ptypes import (
sizeof
)
from macholib.mach_o import (
LC_SEGMENT_64,
load_command,
segment_command_64,
section_64,
dyld_info_command,
symtab_command,
dysymtab_command,
linkedit_data_command
)
from macholib.MachO import (
MachO
)

VM_PROT_NONE = 0x00
VM_PROT_READ = 0x01
VM_PROT_WRITE = 0x02
VM_PROT_EXECUTE = 0x04

SEG_LINKEDIT = b'__LINKEDIT'

def align(size, base):
over = size % base
if over:
return size + (base - over)
return size

def copy_io(src, dst, size=None):
blocksize = 2 ** 23
if size is None:
while True:
d = src.read(blocksize)
if not d:
break
dst.write(d)
else:
while size:
s = min(blocksize, size)
d = src.read(s)
if len(d) != s:
raise Exception('Read error')
dst.write(d)
size -= s

def vmsize_align(size):
return align(max(size, 0x4000), 0x1000)

def cstr_fill(data, size):
if len(data) > size:
raise Exception('Pad error')
return data.ljust(size, b'\x00')

def find_linkedit(commands):
for i, cmd in enumerate(commands):
if not isinstance(cmd[1], segment_command_64):
continue
if cmd[1].segname.split(b'\x00')[0] == SEG_LINKEDIT:
return (i, cmd)

def shift_within(value, amount, within):
if value < within[0] or value > (within[0] + within[1]):
return value
return value + amount

def shift_commands(commands, amount, within, shifts):
for (Command, props) in shifts:
for (_, cmd, _) in commands:
if not isinstance(cmd, Command):
continue
for p in props:
v = getattr(cmd, p)
setattr(cmd, p, shift_within(v, amount, within))

def main(args):
if len(args) <= 5:
print('Usage: macho_in macho_out segname sectname sectfile')
return 1
(_, macho_in, macho_out, segname, sectname, sectfile, *optional) = args

with contextlib.ExitStack() as stack:
fi = stack.enter_context(open(macho_in, 'rb'))
fo = stack.enter_context(open(macho_out, 'wb'))
fs = stack.enter_context(open(sectfile, 'rb'))

macho = MachO(macho_in)
if macho.fat:
raise Exception('FAT unsupported')
header = macho.headers[0]

# Find the closing segment.
(linkedit_i, linkedit) = find_linkedit(header.commands)
(_, linkedit_cmd, _) = linkedit

# Remember where closing segment data is.
linkedit_fileoff = linkedit_cmd.fileoff

# Find the size of the new segment content.
fs.seek(0, io.SEEK_END)
sect_size = fs.tell()
fs.seek(0)

# Create the new segment with section.
lc = load_command(_endian_=header.endian)
seg = segment_command_64(_endian_=header.endian)
sect = section_64(_endian_=header.endian)
lc.cmd = LC_SEGMENT_64
lc.cmdsize = sizeof(lc) + sizeof(seg) + sizeof(sect)
seg.segname = cstr_fill(segname.encode('ascii'), 16)
seg.vmaddr = linkedit_cmd.vmaddr
seg.vmsize = vmsize_align(sect_size)
seg.fileoff = linkedit_cmd.fileoff
seg.filesize = seg.vmsize
seg.maxprot = int(optional[0]) if len(optional) >= 1 else VM_PROT_READ | VM_PROT_WRITE;
seg.initprot = seg.maxprot
seg.nsects = 1
sect.sectname = cstr_fill(sectname.encode('ascii'), 16)
sect.segname = seg.segname
sect.addr = seg.vmaddr
sect.size = sect_size
sect.offset = seg.fileoff
sect.align = 0 if sect_size < 16 else 4

# Shift closing segment down.
linkedit_cmd.vmaddr += seg.vmsize
linkedit_cmd.fileoff += seg.filesize

# Shift any offsets that could reference that segment.
shift_commands(
header.commands,
seg.filesize,
(linkedit_fileoff, linkedit_cmd.filesize),
[
(dyld_info_command, [
'rebase_off',
'bind_off',
'weak_bind_off',
'lazy_bind_off',
'export_off'
]),
(symtab_command, [
'symoff',
'stroff'
]),
(dysymtab_command, [
'tocoff',
'modtaboff',
'extrefsymoff',
'indirectsymoff',
'extreloff',
'locreloff'
]),
(linkedit_data_command, [
'dataoff'
])
]
)

# Update header and insert the segment.
header.header.ncmds += 1
header.header.sizeofcmds += lc.cmdsize
header.commands.insert(linkedit_i, (lc, seg, [sect]))

# Write the new header.
header.write(fo)

# Copy the unchanged data.
fi.seek(fo.tell())
copy_io(fi, fo, linkedit_fileoff - fo.tell())

# Write new section data, padded to segment size.
copy_io(fs, fo, sect_size)
fo.write(b'\x00' * (seg.filesize - sect_size))

# Copy remaining unchanged data.
copy_io(fi, fo)

# Copy mode to the new file.
os.chmod(macho_out, os.stat(macho_in).st_mode)

return 0

if __name__ == '__main__':
sys.exit(main(sys.argv))
1 change: 1 addition & 0 deletions tools/extend-mach-o/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
macholib>=1.16.2

0 comments on commit 46a12ff

Please sign in to comment.