├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md └── dyld-shared-cache-extractor.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | /build* 3 | /target 4 | compile_commands.json 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | project(dyld-shared-cache-extractor LANGUAGES C) 3 | 4 | set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version") 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS 1) 7 | 8 | if(NOT CMAKE_BUILD_TYPE) 9 | set(CMAKE_BUILD_TYPE 10 | "RelWithDebInfo" 11 | CACHE STRING "Default build type: RelWithDebInfo" FORCE) 12 | endif() 13 | 14 | add_executable(dyld-shared-cache-extractor dyld-shared-cache-extractor.c) 15 | target_compile_options(dyld-shared-cache-extractor PRIVATE -g -Wall -Wextra) 16 | install(TARGETS dyld-shared-cache-extractor) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Keith Smiley (http://keith.so) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the 'Software'), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dyld-shared-cache-extractor 2 | 3 | [As of macOS Big Sur][mjtsai], instead of shipping the system libraries 4 | with macOS, Apple ships a generated cache of all built in dynamic 5 | libraries and excludes the originals. This tool allows you to extract 6 | these libraries from the cache for reverse engineering. 7 | 8 | ## Usage 9 | 10 | Extract the default shared cache to `/tmp/libraries`: 11 | 12 | ```sh 13 | dyld-shared-cache-extractor \ 14 | /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e \ 15 | /tmp/libraries 16 | ``` 17 | 18 | If this fails it could be because the shared cache format has changed, 19 | and the version you're trying to extract isn't supported by the version 20 | of Xcode you have selected globally (which you can view with 21 | `xcode-select -p` and `xcodebuild -version`). In this case you might 22 | have to download a newer version of Xcode (potentially a beta version if 23 | you're trying to extract the cache from a beta OS version) and override 24 | the Xcode version when running `dyld-shared-cache-extractor`: 25 | 26 | ```sh 27 | DEVELOPER_DIR=/Applications/Xcode-beta.app \ 28 | dyld-shared-cache-extractor \ 29 | /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e \ 30 | /tmp/libraries 31 | ``` 32 | 33 | If you want to prefer the system installation of `dsc_extractor.bundle` 34 | instead of Xcode's version, you can pass it manually on the command 35 | line: 36 | 37 | ```sh 38 | dyld-shared-cache-extractor \ 39 | /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e \ 40 | /tmp/libraries \ 41 | /usr/lib/dsc_extractor.bundle 42 | ``` 43 | 44 | On macOS versions before Ventura the shared cache was in a different 45 | location, you can extract on older macOS versions with: 46 | 47 | ```sh 48 | dyld-shared-cache-extractor \ 49 | /System/Library/dyld/dyld_shared_cache_arm64e \ 50 | /tmp/libraries 51 | ``` 52 | 53 | ## Installation 54 | 55 | [Homebrew](https://brew.sh): 56 | 57 | ```sh 58 | brew install keith/formulae/dyld-shared-cache-extractor 59 | ``` 60 | 61 | Manually: 62 | 63 | ```sh 64 | cmake -B build 65 | cmake --build build 66 | cmake --install build 67 | ``` 68 | 69 | ## More details 70 | 71 | There are a few different ways you can interact with these shared 72 | caches. 73 | 74 | 1. Depending on what you're doing inspecting them in [Hopper][hopper] is 75 | the easiest option 76 | 2. For a bit more functionality you can build the 77 | `dyld_shared_cache_util` target from the latest `dyld` [source 78 | dump][dump], but this requires some [modifications][modifications] 79 | 80 | The problem with the 2 options above is that they can lag behind format 81 | changes in the shared cache. This tool loads the private 82 | `dsc_extractor.bundle` from Xcode, meaning it should always be able to 83 | extract the shared cache files even from beta OS versions (potentially 84 | using a beta Xcode version). 85 | 86 | This logic is based on the function at the bottom of 87 | `dyld3/shared-cache/dsc_extractor.cpp` from the `dyld` [source 88 | dump][dump]. 89 | 90 | [dump]: https://opensource.apple.com 91 | [hopper]: https://www.hopperapp.com 92 | [mjtsai]: https://mjtsai.com/blog/2020/06/26/reverse-engineering-macos-11-0 93 | [modifications]: https://lapcatsoftware.com/articles/bigsur.html 94 | -------------------------------------------------------------------------------- /dyld-shared-cache-extractor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | __attribute__((noreturn)) 11 | __attribute__((__format__(__printf__, 1, 0))) static void 12 | fail(const char *error, ...) { 13 | va_list args; 14 | va_start(args, error); 15 | vfprintf(stderr, error, args); 16 | va_end(args); 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | static int (*extract)(const char *cache_path, const char *output_path, 21 | void (^progress)(int, int)); 22 | 23 | static int get_library_path(const char *candidate, char *output) { 24 | if (candidate) { 25 | if (access(candidate, R_OK) != 0) { 26 | fail("error: dsc_extractor.bundle not found at provided path: %s\n", 27 | candidate); 28 | } 29 | 30 | strncpy(output, candidate, PATH_MAX); 31 | return 0; 32 | } 33 | 34 | FILE *pipe = popen("xcrun --sdk iphoneos --show-sdk-platform-path", "r"); 35 | if (pipe && fgets(output, PATH_MAX, pipe) != NULL) { 36 | output[strlen(output) - 1] = '\0'; 37 | strcat(output, "/usr/lib/dsc_extractor.bundle"); 38 | assert(pclose(pipe) == 0); 39 | return 0; 40 | } 41 | 42 | const char *builtin = "/usr/lib/dsc_extractor.bundle"; 43 | if (access(builtin, R_OK) == 0) { 44 | strncpy(output, builtin, PATH_MAX); 45 | return 0; 46 | } 47 | 48 | return 1; 49 | } 50 | 51 | static int extract_shared_cache(const char *library_path, 52 | const char *cache_path, 53 | const char *output_path) { 54 | void *handle = dlopen(library_path, RTLD_LAZY); 55 | if (!handle) 56 | fail("error: failed to load bundle: %s\n", library_path); 57 | 58 | *(void **)(&extract) = 59 | dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); 60 | 61 | if (!extract) 62 | fail("error: failed to load function from bundle: %s\n", library_path); 63 | 64 | return extract(cache_path, output_path, ^void(int completed, int total) { 65 | printf("extracted %d/%d\n", completed, total); 66 | }); 67 | } 68 | 69 | int main(int argc, char *argv[]) { 70 | if (!(argc == 3 || argc == 4)) 71 | fail("Usage: %s " 72 | "[]\n", 73 | argv[0]); 74 | 75 | const char *shared_cache = argv[1]; 76 | if (access(shared_cache, R_OK) != 0) 77 | fail("error: shared cache path doesn't exist: %s\n", shared_cache); 78 | 79 | const char *library_candidate = argc == 4 ? argv[3] : NULL; 80 | char library_path[PATH_MAX]; 81 | if (get_library_path(library_candidate, library_path) != 0) 82 | fail("error: failed to fetch Xcode path\n"); 83 | 84 | if (access(library_path, R_OK) != 0) 85 | fail("error: dsc_extractor.bundle wasn't found at expected path %s. " 86 | "Install Xcode or provide path as argument\n", 87 | library_path); 88 | printf("dsc_extractor.bundle found at %s\n", library_path); 89 | 90 | const char *output_path = argv[2]; 91 | return extract_shared_cache(library_path, shared_cache, output_path); 92 | } 93 | --------------------------------------------------------------------------------