Because Box has opted not to early-release a patch for Box Drive that is compatible with macOS Big Sur beta, I decided to reverse-engineer the app and patch it myself. Using uncompyle6 and learning a bit about py2app, I was able to get Box Drive functioning again on Big Sur. (see solution on github)
Getting Started
To attempt to get more information about what was causing Box Drive to fail to launch, I tried executing the Box binary directly via the command line. The error output indicated there was some python in play. Based on the output, it appeared the Box app had trouble loading the CoreServices Framework. Hmm let’s see what that about?
%> /Applications/Box.app/Contents/MacOS/Box
Traceback (most recent call last):
File "/Applications/Box.app/Contents/Resources/__boot__.py", line 98, in <module>
_run()
File "/Applications/Box.app/Contents/Resources/__boot__.py", line 82, in _run
exec(compile(source, path, 'exec'), globals(), globals())
File "/Applications/Box.app/Contents/Resources/bfd_main.py", line 21, in <module>
from box.app.sync_app_delegate import SyncAppDelegate
File "box/app/sync_app_delegate/__init__.pyc", line 9, in <module>
File "box/app/sync_app_delegate/mac_sync_app_delegate.pyc", line 13, in <module>
File "LaunchServices/__init__.pyc", line 9, in <module>
File "CoreServices/__init__.pyc", line 15, in <module>
File "CoreServices/LaunchServices/__init__.pyc", line 17, in <module>
File "objc/_dyld.pyc", line 122, in pathForFramework
File "objc/_dyld.pyc", line 117, in dyld_find
File "objc/_dyld.pyc", line 88, in dyld_framework
ImportError: Framework CoreServices could not be found
2020-06-28 23:44:47.787 Box[18421:1504317] Box Error
The root cause
In macOS 11, shared dynamic libraries (frameworks) have been moved into a cache file, so checks to see if they exist on the file system will fail because – well, they don’t exist on the file system anymore. However, to load them from the cache, one must still specifying their paths as if they still existed on the file system. Confusing right? dlopen()
just knows what to do. The following is from the macOS 11 beta release notes:
New in macOS Big Sur 11 beta, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to
https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11-beta-release-notesdlopen()
the path, which will correctly check for the library in the cache. (62986286)
It’s good to know that dlopen()
will handle loading the dynamic libraries from the cache, because the LoadLibrary
method from Python’s ctypes.cdll module utilizes dlopen()
. This will work to our advantage later.
Slight tangent – Reverse-engineering py2app bundles
The Box application (/Applications/Box.app/Contents/MacOS/Box
) was a mach-o binary, not python. The .pyc
files listed in error output were not present anywhere in the Box app’s subfolders. So, then my goal became finding out how the Box app was packaged from Python source code; this would help me understand how to get to the python modules. After reviewing some information on freezing python code, I identified possible tools that could have been used to “freeze” Box.
I came across the py2app bundler. When I used grep to search for the keyword py2app, there was a hit on the Box mach-o binary. So, then I continued on the assumption that I was working with a py2app bundled application. After reading further into py2app, I learned I should find what I was looking for (the compiled python modules) in a zip archive located in the <app_root>/Content/Resources/lib/
directory, namely /Applications/Box.app/Content/Resources/lib/python37.zip
. Once I copied the zip to a staging location and unzipped it, I could see all the .pyc
files I was looking for.
The Patching Methodology (py2app)
It might be helpful to some if I describe the general process I’ll be following.
- decompile the offending file(s) such as
objc/_dyld.pyc
as referenced in the error output. - modify the python code
- recompile the modified python code
- overwrite the original
.pyc
file(s) - zip up all the .pyc files
- Replace the original python37.zip with mine.
- Test the Box app to see if any new errors arise.
- Repeat if necessary
Applying the Methodology
I used uncompyle6 to decompile the .pyc
files I was interested in, modified the code accordingly, and used the command python3.7 -m compileall <filepath>
to recompile the modified code. Then, I overwrote the original .pyc
file with my new one and zipped up the staging directory and replaced the original python37.zip with mine. Once I worked out all the Frameworks issues, the Box app still encountered some run-time issues; the app would launch, the Box Drive file system would mount, but there was also an error dialog box that popped up and Box Drive would eventually crash.
To troubleshoot these issues, I had to use the Box app’s logging capabilities, and identify errors in the output. It turned out there was additional code that needed patching. Here’s a summary of shell commands I used to accomplish what I mentioned above, including replacing python37.zip, and launching the Box app with verbose logging: uncompyle6 -o /tmp objc/_dyld.pyc
vi /tmp/_dyld.py #modify python code
python3.7 -m compileall /tmp/_dyld.py
cp /tmp/__pycache__/_dyld.pyc objc/_dyld.pyc
zip -r ../python37_.zip . #While in the staging directory where python37.zip was unzipped
sudo mv ../python37_.zip /Applications/Box.app/Contents/Resources/lib/python37.zip; /Applications/Box.app/Contents/MacOS/Box -c -v -lf /tmp/box.log
I’ve done this work already and made the replacement python37.zip available along with the a script that copies it to the appropriate path. You can find these at my github repo https://github.com/slw07g/boxdrive-patch.
Nuances
- the
37
inpython37.zip
refers to the major and minor version of Python that compiled the python code. Thus, one would need to use Python 3.7 to compile the patched code (or alternatively decompile and recompile every file with your preference of python version) - The Box Fuse Kernel extension also doesn’t load automatically on Big Sur. I describe this manual step in the README file located in my github repo.
Shanief is a seasoned cyber security professional, with over 8 years of diverse experience in enterprise intrusion detection, response and threat hunting.