Datacard sp35 drivers for mac. 32-bit programs on macOS Mojave are probably the most obscure configuration for Mac software. Due to various changes in Mojave, previous resources to inject into 32-bit programsare no longer functional. There have been posts on injecting into 64-bit programs, but the 32-bit resources have not been updated. This post details our work on writing a library injection tool for 32-bit applications on macOS Mojave.
The hard problems in injecting into a process on macOS are (in order of execution):
- Find the pid of our target process
- Acquire a mach task port to the target process
- Create a remote memory region for shellcode and stack
- Spawn a remote thread with the code
- Run shellcode that calls
dlopen
and calls the entry point function - Clean up
Finding the Target
- Mojave is the last version of Apple's macOS to support 32-bit apps. To continue to run 32-bit apps on macOS Catalina+, try a virtual machine.
- Over the past couple of OS releases, Apple has made it increasingly clear that 32-bit applications are on the way out. Starting with macOS High Sierra 10.13.4, launching a 32-bit application for the first time will result in a message similar to this being displayed: macOS High Sierra 10.13.4 and later macOS Mojave 10.14.x When.
Macos Mojave Iso
“At our Worldwide Developers Conference in 2017, Apple informed developers that macOS High Sierra would be the last version of macOS to run 32-bit apps without compromise.” At the time it was not clear what Apple meant by “compromise,” but there was speculation that 32-bit apps might run in macOS Mojave with some sort of emulation. Jun 05, 2018 32-bit app warning in macOS 10.14 Mojave (beta 1) Apple's effort to phase them out on Macs mirrors the path it took when ending 32-bit app support on iOS devices.
Cannot access photoshop app on my mac. macOS has a convenient API for finding a process based on its identifier. This allows us to automatically determine the pid of our target without needing to look it up manually:
Acquiring a Task Port
Luckily for us, this part has already been done. The technique we’re using is based on Scott Knight’s blog post about injecting 64-bit processes.
This requires either running the injection program as root or signing the target program with the
com.apple.security.get-task-allow
entitlement. https://skillsbrown143.weebly.com/squidman-app-for-mac.html. Even then, we cannot inject into Apple-protected processes like Finder.app or Dock.app due to SIP, even running as root. There may also be issues with processes that are compiled with Hardened Runtime; we did not test those.In our case, we don’t control the target program so we are running the injection program as root. Clean mac app store cache.
Loading Remote Memory
This has also already been done for us in Knight’s blog post.
Spawning a Remote Thread
Hdmi converter for mac. Again following Knight, but replacing all the 64-bit structures with 32-bit structures.
First we set up the memory:
Then we set up a thread:
Then we start the thread:
Allen bradley vfd powerflex 525 user manual. This is all we need to create a remote thread with a stack and code.
Shellcode
At this point, we have to write the code that our injected thread will run. This comes in the form of shellcode, as we cannot simply inject a library. Ideally this code should call
dlopen
on a user-specified library path.Due to the dual-kernel nature of XNU, creating a thread with
thread_create_running
leaves you with a broken thread that exists only in the Mach kernel with no counterpart in the BSD kernel. Because of this, most syscalls will crash the process if called. Prior to macOS Mojave, you could call _pthread_set_self(NULL)
(or __pthread_set_self(NULL)
before 10.12) on the thread and regain this functionality, but this is no longer possible. Instead, as discovered by Knight, _pthread_set_self
will just crash the process if passed NULL
. So instead, we are going to use pthread_create_from_mach_thread
as he did and create a new, non-broken thread for our real payload.This splits our shellcode into two payloads:
- Initial code that only calls
pthread_create_from_mach_thread
from the broken thread - Stage 2 code that loads our library with
dlopen
Shellcode: Stage 1
In order to preserve sanity while writing shellcode for broken threads on an injected process, we opted not to write assembly by hand but instead use the Shellcode Compiler included with Binary Ninja (shameless plug). It allowed us to write C code and automatically compile it into shellcode without having to write x86 by hand.
There were a few key tricks in writing this payload:
- Marking functions as __stdcall, because by default, scc uses a different convention.
- Assigning external function pointers to placeholder values. In the injector, we will replace these with real pointers.
- We cannot terminate this thread because it is too broken. So instead we loop indefinitely until another thread can kill it later.
Shellcode: Stage 2
After creating a Real PThread (TM), we can start calling functions. Confusingly,
dlopen
decided to crash if its thread did basically anything else, so we instead spawn a new pthread that only calls dlopen
. This seems to satisfy it. Also, now that we are in a real pthread, we can use dlsym to resolve functions without needing to resolve them beforehand.We can then use the result of
dlopen
with dlsym
to find our injected library’s entry point. Since the injected library takes care of cleaning up the broken injection threads, we spawn its entry point in another thread and wait for it to clean up the stage 2 thread. We also put a pointer somewhere in our shellcode segment to the entry point so it can find the two shellcode threads and terminate them.![Run 32 bit apps on macos mojave 10.14 Run 32 bit apps on macos mojave 10.14](https://www.digitel.net/media/blog/mac_catalina.jpg)
Finally, after all this time, we have execution as C in a real library.
Cleaning Up the Threads: Stage 3
After we have started execution in our library, we are running in the context of the target process and can easily find its threads. From there, we can just iterate through them and kill any that have their
$eip
in the same page as the shellcode.Resolving Functions
Now that we have a payload, we just need to dynamically resolve its external functions before we run it. Normally, macOS makes this pretty easy with system libraries and the dyld shared cache, but if we try to run code from a debugger this method will not work, as lldb injects its own versions of
libsystem_pthread.dylib
into our debugged process. So instead we resolve it manually. We based our resolver on Stanislas Lejay’s “Playing with Mach-O binaries and dyld”, but updated it to read memory from the target process over a mach task port.First, a convenience helper for reading virtual memory from the target process:
Then to start resolving functions, we need to find the base address of a library in the target process. Again, based off “Playing with Mach-O binaries and dyld”: Swish mac os app download.
Then we can put these parts together and find the address of any function in the target process:
Patching Shellcode
Now that we have resolved addresses, we need to patch them into the shellcode. Additionally, we need to patch in the various strings for library paths. Megadownloader for mac.
First, we define an address where we can put the path of our Stage 3 library and an address for the parameters for
inj_entry
, then write those:Then, a list of remappings that match strings in the shellcode to be replaced with addresses:
Notably, this contains:
- The various external functions we need to call
- The location of the Stage 2 shellcode
- The location of the string for the injection library that is dlopen()ed
- The location of the parameters to
inj_entry
Then, we just iterate through the shellcode and check each offset against each remapping pattern and replace the bytes as requested:
Automating Shellcode with SCC
During the process of writing this injection framework, we needed to test a lot of shellcode. Conveniently,
scc
comes with a command-line interface capable of being scripted from python by means of subproccess.run()
:From here, we can simply format the shellcode output into a C-style header file that our injection process can
#include
. Additionally we define a few extra variables to assist the injection code:Conclusion
Injecting into a remote process on Windows seems trivial when compared to the mess that is macOS. This process involved shellcode, broken half-threads, and about 10 hours of reading Mach documentation that barely exists. And in the end, we now have a tool for injecting into 32-bit applications on the last version of macOS to support 32-bit applications.
References
Source code: GitHub
Run 32 Bit Apps On Macos Mojave 10.13
Apple began transitioning to 64-bit hardware and software technology for Mac over a decade ago, and all modern Macs now include powerful 64-bit processors that can run advanced 64-bit apps. These apps can access dramatically more memory, enable faster system performance, and take advantage of technologies that define today's Mac experience.
Apple has been working with developers to transition their apps, and in 2018 Apple informed them that macOS Mojave would be the last version of macOS to run 32-bit apps. Starting with macOS Catalina, 32-bit apps are no longer compatible with macOS.
If you get an alert about a 32-bit app
You will see one of these alerts when attempting to open a 32-bit app:
[app name] needs to be updated.
The developer of this app needs to update it to work with this version of macOS. Contact the developer for more information.
The developer of this app needs to update it to work with this version of macOS. Contact the developer for more information.
Run 32 Bit Apps On Macos Mojave 10.14
[app name] is not optimized for your Mac and needs to be updated.
This app will not work with future versions of macOS and needs to be updated to improve compatibility. Contact the developer for more information.
This app will not work with future versions of macOS and needs to be updated to improve compatibility. Contact the developer for more information.
When installing macOS, you may see a list of recently used apps that are 32-bit. You can review this list before deciding to continue installation. You may also see a prohibitory symbol over the icon of each 32-bit app in the Finder, letting you know that the app will not open.
For all 32-bit apps, please contact the developer of the app to learn whether an updated version of their software is available or planned.