Reverse engineering macOS is weird because Apple history is weird.
You are not looking at one operating system.
You are looking at several different worlds stacked on top of each other:
- the original Macintosh system from the 1980s
- Classic Mac OS with resource forks, extensions, traps, and cooperative multitasking
- PowerPC-era Mac OS with Code Fragment Manager and bigger development tools
- Mac OS X, which brought NeXTSTEP, Unix, Mach, BSD, Objective-C, and bundles
- modern macOS with SIP, sandboxing, notarization, hardened runtime, dyld shared cache, Swift, and Apple Silicon
That is why “reversing Mac OS” is not one topic.
It is a timeline.
And honestly, that timeline is more interesting than another generic article about running strings on a binary.
This article is a practical overview of how Mac debugging and reverse engineering evolved, why Classic Mac OS was so different, how Mac OS X changed everything, and what modern macOS reversing looks like today.
Not as malware drama.
Not as piracy nostalgia.
As operating system archaeology.
First: What Do We Mean by Reversing?
Reverse engineering means studying software from the outside.
You may not have the source code, so you inspect:
- binary structure
- symbols
- strings
- resources
- function calls
- frameworks
- system behavior
- file formats
- memory
- logs
- crashes
- network behavior
- UI behavior
On macOS, this can mean many different things.
You might reverse:
- an old Classic Mac OS application
- a PowerPC-era game
- a Carbon application
- a Cocoa Objective-C app
- a Swift application
- a kernel extension
- an Apple framework
- a private API
- a file format
- an installer
- a crash log
- a system daemon
- a launch agent
- a suspicious binary
- your own app when debugging symbols are gone
Some of that is normal development work.
Some of it is security research.
Some of it is retro computing.
Some of it is just curiosity.
The important line is simple: do it on software and machines you are allowed to analyze. Do not use reverse engineering as a cover story for stealing software, bypassing licensing, or breaking into systems.
That part is boring, but it has to be said.
The Classic Mac OS World Was Different
Classic Mac OS was not Unix.
This is the first thing modern developers need to understand.
If you come from Linux or modern macOS, your mental model is probably:
- processes
- protected memory
- files in directories
- permissions
- dynamic libraries
- command-line tools
- debuggers
- logs
- crash reports
- system calls
Classic Mac OS did not feel like that.
The early Mac was a small personal computer system designed around a graphical interface, a single-user environment, toolbox APIs, resources, and tight hardware/software integration.
It was elegant in some ways and terrifying in others.
Classic Mac OS did not have modern protected memory in the way we expect today. One bad app or extension could take the whole system down. System extensions could patch behavior. Applications used resource forks. Much of the system was based around the Macintosh Toolbox and low-level traps.
This made debugging feel very different.
When something crashed, it often crashed the whole experience.
You were not always debugging a process.
You were debugging the machine.
Resource Forks and ResEdit: The Gentle Door Into Reversing
For many people, the first taste of Mac reverse engineering was not assembly language.
It was ResEdit.
Classic Mac applications often stored interface elements, icons, menus, dialogs, strings, sounds, and other structured data in resources. These lived in the resource fork of a file.
That alone already sounds alien to modern users.
Today we usually think of a file as one stream of bytes. Classic Mac OS files could have two forks:
- data fork
- resource fork
The data fork held normal data.
The resource fork held structured resources.
ResEdit was Apple’s resource editor. It let people open files and inspect or modify those resources. You could look at icons, menus, strings, dialogs, and other pieces of an application without fully disassembling the program.
For a curious Mac user, this was magic.
You could open an app and see parts of its personality.
Not source code.
Not full logic.
But enough to feel like you were peeking behind the curtain.
This was one of the reasons the old Mac ecosystem had a very particular hacking culture. People customized apps, patched resources, changed strings, modified icons, explored games, and learned how software was assembled.
It was not always “professional reverse engineering.”
Sometimes it was curiosity with a GUI.
That matters.
A tool like ResEdit made software feel inspectable.
Modern software often feels sealed, signed, sandboxed, notarized, and compressed into frameworks inside bundles inside caches inside security policies. Classic Mac OS was much more approachable.
Not safer.
Approachable.
Different thing.
MacsBug: Debugging at the Machine Level
If ResEdit was the friendly door, MacsBug was the basement.
MacsBug was the low-level debugger for Classic Mac OS. It was an assembly-level debugger used to inspect memory, registers, stacks, code, and crashes.
When a Mac crashed badly, MacsBug could drop you into a debugger prompt instead of just leaving you with a frozen machine.
This is where debugging felt very direct.
You could:
- inspect memory
- examine registers
- disassemble instructions
- step through code
- trace execution
- look at stack frames
- inspect system state
- try to understand why the machine died
For developers, MacsBug was a serious tool.
For power users, it was also a strange symbol of old Mac depth. If you had MacsBug installed, a crash did not just produce sadness. It produced a prompt. You could type commands. You could look around. You might not understand everything, but the system was suddenly speaking machine language to you.
That is a very different debugging culture than modern “the app unexpectedly quit, send report to Apple.”
MacsBug was not comfortable.
It was not friendly.
But it was honest.
The machine crashed, and there you were, staring at the evidence.
The Programmer’s Key Era
Old Macs had a very physical relationship with debugging.
Some machines had a programmer’s key or interrupt switch. You could drop into a debugger or interrupt execution using hardware-level controls.
That sounds crazy now.
Imagine explaining to a modern MacBook user:
There used to be a physical way to interrupt the machine and enter a debugger.
Today Apple tries to hide complexity from normal users. That is usually good. Most people should not see registers and stack dumps.
But for developers and hardware nerds, that old world had charm.
The machine felt like an object you could argue with directly.
Not through layers of permissions, signing, profiles, SIP, and GUI dialogs.
Directly.
Debugging Classic Mac OS Was Also Painful
We should not romanticize too much.
Classic Mac OS debugging was painful.
The system was fragile. Memory corruption could destroy everything. Extensions could conflict. One app could poison the whole environment. Cooperative multitasking meant a misbehaving app could make the system feel dead. Debugging graphical apps in a shared memory world could be miserable.
Modern macOS is stricter and more locked down, but it is also much more robust.
A crashing app usually does not kill the entire machine.
Protected memory matters.
Preemptive multitasking matters.
Crash reports matter.
Symbolicated stacks matter.
Modern developer tools matter.
So yes, the old tools were fascinating.
But a lot of the nostalgia comes from people forgetting how much pain those systems could cause.
Classic Mac OS had magic.
It also had landmines.
PowerPC, Code Fragment Manager, and Bigger Apps
The PowerPC era made the Mac more powerful and more complicated.
Classic Mac OS had to move from 68k code to PowerPC code while keeping compatibility with older software. This created a strange mixed world of emulation, native code, fat binaries, and runtime systems.
The Code Fragment Manager was part of that world. It loaded fragments of executable code and prepared them for execution. A fragment could be an application, import library, system extension, or another block of executable code and data.
For reverse engineering, this meant the binary world was no longer just “old 68k app with resources.”
Now you had:
- 68k code
- PowerPC code
- fat binaries
- shared libraries
- CFM fragments
- more complex development tools
- bigger applications
- more serious commercial software
Tools evolved too.
Metrowerks CodeWarrior became a huge part of Mac development in the 1990s. MacsBug remained important. Other tools like Jasik’s Debugger, resource editors, disassemblers, and hex editors became part of the reverse engineering toolbox.
If you were reversing a PowerPC-era Mac app, you were dealing with a transition platform.
Old Mac ideas were still there.
But the software was becoming more modern and more complex.
Then Apple Bought NeXT and Everything Changed
The biggest break in Mac debugging history was not one debugger.
It was Mac OS X.
Apple did not simply modernize Classic Mac OS.
Apple moved to a NeXT-derived system based on Darwin, XNU, Mach, BSD userland pieces, Objective-C frameworks, and a completely different runtime model.
This changed reverse engineering completely.
Classic Mac OS and Mac OS X are historically connected, but technically they are very different worlds.
Mac OS X brought:
- protected memory
- preemptive multitasking
- Unix tools
- Mach-O binaries
- dynamic libraries
- frameworks
- bundles
- Objective-C runtime
- BSD-style command-line environment
- crash reports
- process isolation
- stronger developer tools
For reversers, this was a massive shift.
Suddenly the Mac was much closer to Unix.
You could use terminal tools. You could inspect processes. You could use debuggers in a more familiar way. You had file paths, permissions, daemons, logs, frameworks, and dynamic loading.
But it was not just Unix.
It was Apple’s Unix.
That distinction matters.
Mach-O: The macOS Binary Format
Modern macOS applications use Mach-O binaries.
If you reverse macOS software, you need to understand Mach-O at least at a basic level.
Mach-O is the executable format used for macOS, iOS, and other Apple platforms. It describes code, data, symbols, dynamic library dependencies, load commands, segments, sections, and other metadata needed by the loader.
This is the macOS equivalent of learning PE on Windows or ELF on Linux.
You do not need to become a Mach-O scholar on day one.
But you should understand that a macOS binary contains things like:
- headers
- load commands
- segments
- sections
- symbol tables
- dynamic library references
- Objective-C metadata
- Swift metadata
- code signing information
- architecture slices in universal binaries
Useful built-in tools include:
file SomeApp
otool -L SomeApp
otool -l SomeApp
nm SomeApp
strings SomeApp
codesign -dv SomeApp
Those commands are not “hacking magic.”
They are basic inspection.
They tell you what you are looking at.
Before reversing anything, you need to answer simple questions:
- What architecture is this binary?
- Is it x86_64, arm64, or universal?
- What libraries does it load?
- Does it contain symbols?
- Is it stripped?
- Is it signed?
- Is it a GUI app bundle?
- Is it a command-line tool?
- Is it Objective-C, Swift, C++, or a mixture?
Half of reversing is just knowing what object is on the table.
App Bundles Changed the Shape of Mac Software
On modern macOS, an application is often a bundle.
It looks like one .app file in Finder, but inside it is a directory structure.
A typical app bundle contains:
SomeApp.app/
Contents/
Info.plist
MacOS/
SomeApp
Resources/
Frameworks/
PlugIns/
_CodeSignature/
This is one of the most important things for beginners.
A Mac app is often not “a file.”
It is a folder pretending to be a file.
For reverse engineering, the bundle tells you a lot:
Info.plistdescribes bundle metadataContents/MacOS/contains the main executableResources/contains images, strings, nibs, storyboards, assets, configsFrameworks/may contain bundled librariesPlugIns/may contain extensions_CodeSignature/relates to code signing
This is the modern descendant of the old Mac idea that apps carry structured resources.
The implementation changed completely.
But the design spirit is still there: an app is more than one raw executable.
Objective-C Made Reversing Weirdly Pleasant
Objective-C is one reason macOS reversing became unusually readable compared with many platforms.
Objective-C is dynamic. It has class names, method names, selectors, protocols, and runtime metadata.
If an app is written in Objective-C and not heavily obfuscated, static analysis can reveal a surprising amount:
- class names
- method names
- property names
- selectors
- framework usage
- interface patterns
- delegate methods
- controller structure
This can make reversing Cocoa apps feel almost luxurious compared with stripped C++ binaries.
You may not have source code, but you often have readable names like:
-[LoginViewController submitButtonPressed:]
-[NetworkClient sendRequestWithCompletion:]
-[DocumentManager openRecentFile:]
That is not source.
But it is a map.
Objective-C reversing often starts with the class structure. You look at names, selectors, frameworks, nibs, plists, and messages. Then you connect behavior to code.
This is why many macOS reverse engineers love Objective-C metadata.
Apple accidentally made the platform more inspectable by using a dynamic runtime.
Swift Made It More Complicated Again
Swift changed the story.
Swift binaries can still expose useful metadata, but the names can be more mangled, the runtime is different, and the code can be harder to read depending on compiler settings and optimization.
Swift is not impossible to reverse.
But it is not as immediately friendly as classic Objective-C.
The modern macOS app may include:
- Swift code
- Objective-C code
- C/C++ libraries
- embedded frameworks
- scripting bridges
- Electron components
- helper tools
- launch services
- XPC services
- app extensions
This is why modern reversing is often not just disassembling one binary.
You are mapping a small ecosystem.
GDB to LLDB
For many years, GDB was a common debugger in Unix-like environments, including old Mac OS X development.
But Apple moved to LLDB.
LLDB became the foundation of the Xcode debugging experience around the Xcode 5 era. Today LLDB is the normal debugger on Apple platforms.
For everyday debugging, LLDB is excellent.
You can use it from Xcode or Terminal.
Basic examples:
lldb ./my_program
Then inside LLDB:
run
bt
breakpoint set --name main
continue
thread backtrace
register read
memory read
disassemble
For your own software, this is normal debugging.
For reverse engineering, LLDB becomes useful when you want to observe runtime behavior:
- where does this function get called?
- what arguments are passed?
- what object is this?
- what libraries are loaded?
- what happens before a crash?
- what code path handles this action?
Dynamic analysis answers questions that static analysis cannot.
But modern macOS adds restrictions. You cannot just attach to anything freely. System Integrity Protection, hardened runtime, entitlements, sandboxing, and code signing all affect what you can inspect and modify.
This is where reversing macOS becomes more annoying than reversing a random Linux binary.
Instruments: Debugging Without Feeling Like a Debugger
Apple’s Instruments is not a reverse engineering tool in the classic sense.
It is a performance and behavior analysis tool.
But for understanding macOS apps, it matters.
Instruments can help analyze:
- CPU usage
- memory allocations
- leaks
- file activity
- energy usage
- system trace behavior
- hangs
- responsiveness
- thread behavior
This is very useful when you are trying to understand what an app does without immediately diving into assembly.
Sometimes reversing starts with high-level behavior:
- when does it open files?
- when does it make network requests?
- why does it hang?
- what thread is busy?
- what process wakes up another process?
- what framework is being abused?
For that, Instruments can be more useful than staring at disassembly for six hours.
One of the biggest mistakes beginners make is going too low too quickly.
You do not always need assembly first.
Sometimes you need logs, traces, filesystem monitoring, and process behavior.
DTrace, fs_usage, log, and the macOS Observation Toolbox
macOS has many observation tools.
Some are Apple tools. Some are Unix tools. Some are developer tools.
Examples:
log stream
log show
fs_usage
sample
spindump
vmmap
lsof
nettop
dtrace
Not all of these work the same way on every macOS version. Apple has restricted some low-level tracing over time, especially around system processes and protected areas.
But the general idea remains:
Before you disassemble, observe.
Good reversing often starts with:
- process list
- open files
- network connections
- logs
- child processes
- launch agents
- loaded libraries
- entitlements
- crash reports
- sandbox denials
- filesystem activity
This is not glamorous.
It is effective.
A lot of “reverse engineering” is just careful debugging with no source code.
Static Analysis: Hopper, IDA, Ghidra, and Friends
Modern macOS reversing usually combines Apple tools with third-party static analysis tools.
Common tools include:
- Hopper Disassembler
- IDA Pro
- Ghidra
- Binary Ninja
- radare2/rizin
- class-dump style tools for Objective-C metadata
- jtool-style Mach-O inspection tools
- strings, nm, otool, vtool, codesign
Each tool has its own personality.
IDA is powerful and expensive.
Hopper is popular with Mac reversers and feels very natural on macOS.
Ghidra is free and extremely capable.
Binary Ninja is modern and friendly.
radare2 is powerful if you enjoy pain and cryptic commands.
The practical approach is simple:
Use the tool that helps you answer the question fastest.
Do not turn the tool itself into the hobby unless that is actually your hobby.
The dyld Shared Cache Problem
One of the more annoying parts of modern Apple reversing is the dyld shared cache.
Instead of every system library sitting around as a normal standalone dynamic library in the way beginners expect, Apple uses a shared cache for many system libraries and frameworks.
This improves performance and system behavior.
It also makes reverse engineering more annoying.
If you are trying to inspect Apple frameworks, you may need tools that understand and extract or analyze the dyld shared cache.
This is one of those places where macOS reversing is very Apple-specific.
On Linux, you may inspect shared libraries in /lib and /usr/lib.
On macOS, you quickly learn that Apple has its own way of doing things.
Again: not just Unix.
Apple Unix.
SIP Changed the Game
System Integrity Protection, also known as SIP, changed macOS security significantly.
Before SIP, root felt closer to absolute power.
After SIP, even root is restricted in certain system-protected areas. Apple added protections around system files, processes, kernel extension loading, debugging certain processes, and other sensitive operations.
For normal users, SIP is good.
For malware, SIP is annoying.
For reverse engineers, SIP is also annoying.
That does not mean you should simply disable it as a first step.
This is where the attitude matters.
If you are analyzing your own app, you probably do not need to fight SIP.
If you are doing legitimate low-level research, you may use a dedicated research machine or VM with settings appropriate for that work.
Do not casually weaken your main machine because some blog told you to.
Modern macOS is security layered:
- SIP
- Gatekeeper
- code signing
- notarization
- sandboxing
- entitlements
- hardened runtime
- TCC privacy permissions
- AMFI
- XProtect and other platform protections
This is the world modern Mac reversing lives in.
Not the old “drop into MacsBug and poke memory” world.
Code Signing and Notarization
Modern macOS cares deeply about code identity.
Applications are signed. Developers have certificates. Apps may be notarized. The system checks signatures, entitlements, and trust policies.
For reverse engineering, this matters because modifying an app can break its signature. Attaching a debugger can be restricted. Injecting code can be blocked. Loading plugins can fail. Entitlements can determine what the app is allowed to do.
This is not just security theater.
It affects real analysis.
A beginner may patch one byte in a binary, run it, and wonder why macOS refuses to cooperate.
The answer is often: the signature changed, the hardened runtime does not allow what you are doing, or the app’s entitlements do not match your assumptions.
Modern macOS reversing requires understanding the platform rules.
Not just assembly.
Apple Silicon Changed the Architecture
The Apple Silicon transition added another major shift.
For years, Mac reversing was mostly PowerPC, then x86, then x86_64.
Now arm64 is central.
Universal binaries can contain multiple architecture slices. Rosetta 2 can run many Intel apps on Apple Silicon. Some apps are native arm64. Some are universal. Some are still Intel-only. Some ship helper tools with different architectures.
This means the first reversing question is often:
file SomeApp
You need to know what you are looking at.
An app might contain:
- x86_64 slice
- arm64 slice
- arm64e slice
- embedded frameworks with different slices
- translated execution through Rosetta
- native helper tools
Apple Silicon also brings platform security features and pointer authentication concepts that affect low-level reversing.
If you learned reversing on Intel Macs, Apple Silicon is not just “same thing but different registers.”
It is a different target.
Reversing Classic Mac OS vs Modern macOS
These are almost different hobbies.
Classic Mac OS reversing is about:
- resource forks
- 68k and PowerPC code
- MacsBug
- ResEdit
- traps
- Toolbox APIs
- extensions
- cooperative multitasking
- emulators like Basilisk II and SheepShaver
- old file formats
- old games and applications
- binary archaeology
Modern macOS reversing is about:
- Mach-O
- Objective-C and Swift metadata
- app bundles
- XPC services
- frameworks
- LLDB
- dyld shared cache
- sandboxing
- entitlements
- SIP
- code signing
- notarization
- Apple Silicon
- logs and traces
- security research
They both belong to Apple history.
But they feel nothing alike.
Classic Mac OS reversing feels like opening an old machine and touching the gears.
Modern macOS reversing feels like analyzing a highly controlled platform with many guardrails.
Both are interesting.
For different reasons.
A Practical Beginner Workflow for Modern macOS Reversing
If you want to understand a macOS app, do not start by patching binaries.
Start by mapping it.
1. Identify the app structure
ls -R SomeApp.app/Contents
cat SomeApp.app/Contents/Info.plist
Or use:
plutil -p SomeApp.app/Contents/Info.plist
2. Identify the binary
file SomeApp.app/Contents/MacOS/SomeApp
3. Check linked libraries
otool -L SomeApp.app/Contents/MacOS/SomeApp
4. Check load commands
otool -l SomeApp.app/Contents/MacOS/SomeApp
5. Look for obvious strings
strings SomeApp.app/Contents/MacOS/SomeApp | less
6. Check signing and entitlements
codesign -dv --verbose=4 SomeApp.app
codesign -d --entitlements :- SomeApp.app
7. Observe behavior
log stream --predicate 'process == "SomeApp"'
Use Activity Monitor, Console, Instruments, fs_usage, lsof, and crash reports.
8. Then open a disassembler
Only after you understand the shape of the app should you jump into Hopper, IDA, Ghidra, or Binary Ninja.
This workflow is boring.
That is why it works.
A Practical Beginner Workflow for Classic Mac OS Reversing
For Classic Mac OS, the workflow is different.
You probably want an emulator first:
- Basilisk II for 68k Mac environments
- SheepShaver for PowerPC Classic Mac OS
- QEMU for some PowerPC experiments
Then you look at:
- application resources
- resource fork contents
- file type and creator codes
- strings
- menus and dialogs
- old documentation
- file formats
- 68k or PowerPC disassembly
- MacsBug traces
A classic workflow might be:
- Get the app running in an emulator.
- Make a copy of the disk image.
- Open the app resources with ResEdit or a modern resource tool.
- Inspect menus, strings, dialogs, icons, and resource IDs.
- Trigger behavior in the app and observe changes to files.
- Use MacsBug or a debugger when you need code-level analysis.
- Document what you find.
Retro reversing is slower.
But it is also very satisfying.
Old software tends to have smaller scope, fewer layers, and more visible structure.
Modern apps are often huge ecosystems.
Old apps often feel like one person or a small team built them with their hands.
Debugging as a Cultural Difference
The history of Mac debugging is not just about tools.
It is about what Apple wanted the computer to be.
Classic Mac OS came from a culture of personal computing. The machine was approachable, graphical, and tightly designed. But under the surface, developers needed brutal tools like MacsBug because the system was fragile.
Mac OS X came from a different world: NeXT, Unix, Objective-C, Mach, BSD, and professional developer tooling. Debugging became more structured and more powerful.
Modern macOS comes from a security-first, platform-control world. Debugging is still powerful, but you are working inside Apple’s rules. The system is safer, but less open. More stable, but less hackable. More polished, but more controlled.
That is the trade-off.
Old Mac debugging felt like:
Here is the machine. Try not to crash it.
Modern macOS debugging feels like:
Here is the platform. Please have the correct permissions.
Neither world is perfect.
The Best Tools to Know
For modern macOS:
- Xcode
- LLDB
- Instruments
- Console
- Activity Monitor
logotoolnmstringsfilecodesignplutillipodyldinfo- Hopper
- Ghidra
- IDA Pro
- Binary Ninja
- Frida for permitted dynamic instrumentation
- Objective-C runtime tools
- crash reports and symbolication tools
For Classic Mac OS:
- MacsBug
- ResEdit
- Resorcerer
- CodeWarrior tools
- old disassemblers
- hex editors
- Basilisk II
- SheepShaver
- old Inside Macintosh documentation
- file format notes
- resource fork tools
Do not try to learn all of this at once.
Pick the era first.
Classic Mac OS reversing and modern macOS reversing are different skill trees.
What Makes macOS Reversing Special
macOS reversing is special because Apple systems carry history.
You can see layers:
- old Mac resource culture
- NeXTSTEP object-oriented design
- Unix tooling
- Mach kernel ideas
- BSD components
- Objective-C runtime
- Apple frameworks
- security policy layers
- Swift metadata
- Apple Silicon architecture
Windows reversing has its own depth.
Linux reversing has its own depth.
But macOS is strange because it is both polished consumer platform and Unix-derived developer machine.
It gives you lldb, otool, and dtrace ideas.
Then it gives you SIP, notarization, TCC, code signing, and a sealed system volume.
It invites you in and blocks the hallway at the same time.
Very Apple.
Why This History Matters
You do not need to know MacsBug to reverse a modern Swift app.
You do not need ResEdit to inspect a Mach-O binary.
But the history helps you understand why the Mac is the way it is.
Apple has always balanced two impulses:
- make the computer friendly and controlled
- give developers enough power to build serious software
Sometimes the developer side wins.
Sometimes the control side wins.
Classic Mac OS was friendly but fragile.
Mac OS X was powerful but transitional.
Modern macOS is stable and secure but increasingly locked down.
Debugging tools reflect that.
Reverse engineering tools reflect that.
The platform itself reflects that.
Final Thoughts
Reversing Mac OS is not one discipline.
It is a journey through several Apple eras.
Classic Mac OS gave us resource forks, ResEdit, MacsBug, Toolbox APIs, extensions, 68k and PowerPC weirdness, and a style of debugging that felt close to the metal.
Mac OS X brought Mach-O, Unix tools, Objective-C, frameworks, bundles, protected memory, crash reports, GDB, then LLDB.
Modern macOS added stronger security layers: SIP, Gatekeeper, code signing, notarization, sandboxing, entitlements, hardened runtime, TCC, dyld shared cache complexity, Swift, and Apple Silicon.
The result is a platform that is fascinating to reverse because it is not clean history.
It is layered history.
Old Mac ideas did not fully disappear. They mutated. The resource fork became less central, but app bundles still carry structured resources. Classic low-level debugging faded, but LLDB and Instruments are extremely powerful. The system became more Unix-like, then more Apple-controlled.
And that is what makes macOS interesting.