├── README ├── readkmem.xcodeproj └── project.pbxproj └── readkmem └── main.c /README: -------------------------------------------------------------------------------- 1 | _____ 2 | __|__ |__ ______ ____ _____ 3 | | | || ___|| \ | \ 4 | | \ || ___|| \ | \ 5 | |__|\__\ __||______||__|\__\|______/ 6 | |_____| 7 | _____ 8 | __| __ |__ ____ __ ______ ____ __ 9 | | |/ / || \ / || ___|| \ / | 10 | | \ || \/ || ___|| \/ | 11 | |__|\__\ __||__/\__/|__||______||__/\__/|__| 12 | |_____| 13 | 14 | Readkmem 15 | 16 | A small util to dump kernel memory and kernel binaries 17 | 18 | Uses processor_set_tasks() vulnerability or /dev/kmem to 19 | read kernel memory. 20 | 21 | Copyright (c) fG! - 2012,2013, 2014. All rights reserved. 22 | reverser@put.as - http://reverse.put.as 23 | -------------------------------------------------------------------------------- /readkmem.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DE6BE12C13CCB7D50032DA34 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = DE6BE12B13CCB7D50032DA34 /* main.c */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | DE6BE12513CCB7D50032DA34 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | DE6BE12713CCB7D50032DA34 /* readkmem */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = readkmem; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | DE6BE12B13CCB7D50032DA34 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | DE6BE12413CCB7D50032DA34 /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | DE6BE11C13CCB7D50032DA34 = { 42 | isa = PBXGroup; 43 | children = ( 44 | DE6BE12A13CCB7D50032DA34 /* readkmem */, 45 | DE6BE12813CCB7D50032DA34 /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | DE6BE12813CCB7D50032DA34 /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | DE6BE12713CCB7D50032DA34 /* readkmem */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | DE6BE12A13CCB7D50032DA34 /* readkmem */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | DE6BE12B13CCB7D50032DA34 /* main.c */, 61 | ); 62 | path = readkmem; 63 | sourceTree = ""; 64 | }; 65 | /* End PBXGroup section */ 66 | 67 | /* Begin PBXNativeTarget section */ 68 | DE6BE12613CCB7D50032DA34 /* readkmem */ = { 69 | isa = PBXNativeTarget; 70 | buildConfigurationList = DE6BE13013CCB7D50032DA34 /* Build configuration list for PBXNativeTarget "readkmem" */; 71 | buildPhases = ( 72 | DE6BE12313CCB7D50032DA34 /* Sources */, 73 | DE6BE12413CCB7D50032DA34 /* Frameworks */, 74 | DE6BE12513CCB7D50032DA34 /* CopyFiles */, 75 | ); 76 | buildRules = ( 77 | ); 78 | dependencies = ( 79 | ); 80 | name = readkmem; 81 | productName = readkmem; 82 | productReference = DE6BE12713CCB7D50032DA34 /* readkmem */; 83 | productType = "com.apple.product-type.tool"; 84 | }; 85 | /* End PBXNativeTarget section */ 86 | 87 | /* Begin PBXProject section */ 88 | DE6BE11E13CCB7D50032DA34 /* Project object */ = { 89 | isa = PBXProject; 90 | attributes = { 91 | LastUpgradeCheck = 0510; 92 | }; 93 | buildConfigurationList = DE6BE12113CCB7D50032DA34 /* Build configuration list for PBXProject "readkmem" */; 94 | compatibilityVersion = "Xcode 3.2"; 95 | developmentRegion = English; 96 | hasScannedForEncodings = 0; 97 | knownRegions = ( 98 | en, 99 | ); 100 | mainGroup = DE6BE11C13CCB7D50032DA34; 101 | productRefGroup = DE6BE12813CCB7D50032DA34 /* Products */; 102 | projectDirPath = ""; 103 | projectRoot = ""; 104 | targets = ( 105 | DE6BE12613CCB7D50032DA34 /* readkmem */, 106 | ); 107 | }; 108 | /* End PBXProject section */ 109 | 110 | /* Begin PBXSourcesBuildPhase section */ 111 | DE6BE12313CCB7D50032DA34 /* Sources */ = { 112 | isa = PBXSourcesBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | DE6BE12C13CCB7D50032DA34 /* main.c in Sources */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | /* End PBXSourcesBuildPhase section */ 120 | 121 | /* Begin XCBuildConfiguration section */ 122 | DE6BE12E13CCB7D50032DA34 /* Debug */ = { 123 | isa = XCBuildConfiguration; 124 | buildSettings = { 125 | CLANG_WARN_BOOL_CONVERSION = YES; 126 | CLANG_WARN_CONSTANT_CONVERSION = YES; 127 | CLANG_WARN_EMPTY_BODY = YES; 128 | CLANG_WARN_ENUM_CONVERSION = YES; 129 | CLANG_WARN_INT_CONVERSION = YES; 130 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 131 | GCC_C_LANGUAGE_STANDARD = gnu99; 132 | GCC_OPTIMIZATION_LEVEL = 0; 133 | GCC_PREPROCESSOR_DEFINITIONS = DEBUG; 134 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 135 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 136 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 137 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 138 | GCC_WARN_UNDECLARED_SELECTOR = YES; 139 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 140 | GCC_WARN_UNUSED_FUNCTION = YES; 141 | GCC_WARN_UNUSED_VARIABLE = YES; 142 | MACOSX_DEPLOYMENT_TARGET = 10.6; 143 | ONLY_ACTIVE_ARCH = YES; 144 | SDKROOT = macosx; 145 | }; 146 | name = Debug; 147 | }; 148 | DE6BE12F13CCB7D50032DA34 /* Release */ = { 149 | isa = XCBuildConfiguration; 150 | buildSettings = { 151 | CLANG_WARN_BOOL_CONVERSION = YES; 152 | CLANG_WARN_CONSTANT_CONVERSION = YES; 153 | CLANG_WARN_EMPTY_BODY = YES; 154 | CLANG_WARN_ENUM_CONVERSION = YES; 155 | CLANG_WARN_INT_CONVERSION = YES; 156 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 157 | GCC_C_LANGUAGE_STANDARD = gnu99; 158 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 159 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 160 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 161 | GCC_WARN_UNDECLARED_SELECTOR = YES; 162 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 163 | GCC_WARN_UNUSED_FUNCTION = YES; 164 | GCC_WARN_UNUSED_VARIABLE = YES; 165 | MACOSX_DEPLOYMENT_TARGET = 10.6; 166 | SDKROOT = macosx; 167 | }; 168 | name = Release; 169 | }; 170 | DE6BE13113CCB7D50032DA34 /* Debug */ = { 171 | isa = XCBuildConfiguration; 172 | buildSettings = { 173 | ALWAYS_SEARCH_USER_PATHS = NO; 174 | COPY_PHASE_STRIP = NO; 175 | GCC_DYNAMIC_NO_PIC = NO; 176 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 177 | MACOSX_DEPLOYMENT_TARGET = 10.6; 178 | ONLY_ACTIVE_ARCH = NO; 179 | PRODUCT_NAME = "$(TARGET_NAME)"; 180 | }; 181 | name = Debug; 182 | }; 183 | DE6BE13213CCB7D50032DA34 /* Release */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | COPY_PHASE_STRIP = YES; 188 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 189 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 190 | MACOSX_DEPLOYMENT_TARGET = 10.6; 191 | PRODUCT_NAME = "$(TARGET_NAME)"; 192 | }; 193 | name = Release; 194 | }; 195 | /* End XCBuildConfiguration section */ 196 | 197 | /* Begin XCConfigurationList section */ 198 | DE6BE12113CCB7D50032DA34 /* Build configuration list for PBXProject "readkmem" */ = { 199 | isa = XCConfigurationList; 200 | buildConfigurations = ( 201 | DE6BE12E13CCB7D50032DA34 /* Debug */, 202 | DE6BE12F13CCB7D50032DA34 /* Release */, 203 | ); 204 | defaultConfigurationIsVisible = 0; 205 | defaultConfigurationName = Release; 206 | }; 207 | DE6BE13013CCB7D50032DA34 /* Build configuration list for PBXNativeTarget "readkmem" */ = { 208 | isa = XCConfigurationList; 209 | buildConfigurations = ( 210 | DE6BE13113CCB7D50032DA34 /* Debug */, 211 | DE6BE13213CCB7D50032DA34 /* Release */, 212 | ); 213 | defaultConfigurationIsVisible = 0; 214 | defaultConfigurationName = Release; 215 | }; 216 | /* End XCConfigurationList section */ 217 | }; 218 | rootObject = DE6BE11E13CCB7D50032DA34 /* Project object */; 219 | } 220 | -------------------------------------------------------------------------------- /readkmem/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * _____ _____ 3 | * __|__ |__ ______ ____ _____ __| __ |__ ____ __ ______ ____ __ 4 | * | | || ___|| \ | \ | |/ / || \ / || ___|| \ / | 5 | * | \ || ___|| \ | \| \ || \/ || ___|| \/ | 6 | * |__|\__\ __||______||__|\__\|______/|__|\__\ __||__/\__/|__||______||__/\__/|__| 7 | * |_____| |_____| 8 | * 9 | * Readkmem 10 | * 11 | * A small util to dump kernel memory and kernel binaries 12 | * 13 | * Copyright (c) fG! - 2012, 2013, 2014. All rights reserved. 14 | * reverser@put.as - http://reverse.put.as 15 | * 16 | * Note: This requires kmem/mem devices to be enabled if processor_set_tasks() vuln not available 17 | * 18 | * Edit /Library/Preferences/SystemConfiguration/com.apple.Boot.plist 19 | * add kmem=1 parameter, and reboot! 20 | * 21 | * v0.1 - Initial version 22 | * v0.2 - Some fixes 23 | * v0.3 - More improvements, more useful now! 24 | * v0.4 - Code cleanups 25 | * v0.5 - Add feature to dump mach-o binaries from kernel space 26 | * Code cleanups 27 | * v0.6 - Try to use processor_set_tasks() vulnerability to read kernel memory 28 | * before trying to use /dev/kmem 29 | * 30 | * Redistribution and use in source and binary forms, with or without 31 | * modification, are permitted provided that the following conditions 32 | * are met: 33 | * 1. Redistributions of source code must retain the above copyright 34 | * notice, this list of conditions and the following disclaimer. 35 | * 2. Redistributions in binary form must reproduce the above copyright 36 | * notice, this list of conditions and the following disclaimer in the 37 | * documentation and/or other materials provided with the distribution. 38 | * 3. The name of the author may not be used to endorse or promote products 39 | * derived from this software without specific prior written permission. 40 | * 41 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 42 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 44 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 45 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 47 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 48 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 49 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 50 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 | * 52 | */ 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | #define VERSION "0.6" 73 | 74 | #define ERROR_MSG(fmt, ...) fprintf(stderr, "[ERROR] " fmt " \n", ## __VA_ARGS__) 75 | #define OUTPUT_MSG(fmt, ...) fprintf(stdout, fmt " \n", ## __VA_ARGS__) 76 | #if DEBUG == 0 77 | # define DEBUG_MSG(fmt, ...) do {} while (0) 78 | #else 79 | # define DEBUG_MSG(fmt, ...) fprintf(stdout, "[DEBUG] " fmt "\n", ## __VA_ARGS__) 80 | #endif 81 | 82 | #define x86 0 83 | #define x64 1 84 | 85 | struct mem_source 86 | { 87 | int fd; 88 | mach_port_t kernel_port; 89 | } g_kmem_source; 90 | 91 | mach_vm_address_t vmaddr_slide = 0; 92 | 93 | void header(void); 94 | int get_kernel_type(void); 95 | void readkmem(void *buffer, mach_vm_address_t target_addr, size_t size); 96 | void usage(void); 97 | static size_t get_image_size(mach_vm_address_t address); 98 | static void dump_binary(mach_vm_address_t address, void *buffer); 99 | 100 | /* 101 | * we need to find the binary file size 102 | * which is taken from the filesize field of each segment command 103 | * and not the vmsize (because of alignment) 104 | * if we dump using vmaddresses, we will get the alignment space into the dumped 105 | * binary and get into problems :-) 106 | */ 107 | static size_t 108 | get_image_size(mach_vm_address_t address) 109 | { 110 | // allocate a buffer to read the header info 111 | // NOTE: this is not exactly correct since the 64bit version has an extra 4 bytes 112 | // but this will work for this purpose so no need for more complexity! 113 | struct mach_header header = {0}; 114 | readkmem(&header, address, sizeof(struct mach_header)); 115 | 116 | uint16_t mach_header_size = sizeof(struct mach_header); 117 | switch (header.magic) 118 | { 119 | case MH_MAGIC: 120 | { 121 | break; 122 | } 123 | case MH_MAGIC_64: 124 | { 125 | mach_header_size = sizeof(struct mach_header_64); 126 | break; 127 | } 128 | default: 129 | { 130 | ERROR_MSG("Target is not a mach-o binary!"); 131 | exit(-1); 132 | } 133 | } 134 | 135 | size_t imagefilesize = 0; 136 | // read the load commands 137 | uint8_t *loadcmds = malloc(header.sizeofcmds); 138 | if (loadcmds == NULL) 139 | { 140 | ERROR_MSG("Failed to allocate memory (%s).", __FUNCTION__); 141 | exit(-1); 142 | } 143 | 144 | readkmem(loadcmds, address+mach_header_size, header.sizeofcmds); 145 | 146 | // process and retrieve address and size of linkedit 147 | uint8_t *loadCmdAddress = 0; 148 | // first load cmd address 149 | loadCmdAddress = (uint8_t*)loadcmds; 150 | struct load_command *loadCommand = NULL; 151 | struct segment_command *segCmd = NULL; 152 | struct segment_command_64 *segCmd64 = NULL; 153 | // process commands to find the info we need 154 | for (uint32_t i = 0; i < header.ncmds; i++) 155 | { 156 | loadCommand = (struct load_command*)loadCmdAddress; 157 | // 32bits and 64 bits segment commands 158 | if (loadCommand->cmd == LC_SEGMENT) 159 | { 160 | segCmd = (struct segment_command*)loadCmdAddress; 161 | if (strcmp((char*)(segCmd->segname), "__PAGEZERO") != 0) 162 | { 163 | if (strcmp((char*)(segCmd->segname), "__TEXT") == 0) 164 | { 165 | vmaddr_slide = address - segCmd->vmaddr; 166 | } 167 | imagefilesize += segCmd->filesize; 168 | } 169 | } 170 | else if (loadCommand->cmd == LC_SEGMENT_64) 171 | { 172 | segCmd64 = (struct segment_command_64*)loadCmdAddress; 173 | if (strcmp((char*)(segCmd64->segname), "__PAGEZERO") != 0) 174 | { 175 | if (strcmp((char*)(segCmd64->segname), "__TEXT") == 0) 176 | { 177 | vmaddr_slide = address - segCmd64->vmaddr; 178 | } 179 | imagefilesize += segCmd64->filesize; 180 | } 181 | } 182 | // advance to next command 183 | loadCmdAddress += loadCommand->cmdsize; 184 | } 185 | end: 186 | free(loadcmds); 187 | return imagefilesize; 188 | } 189 | 190 | 191 | /* 192 | * dump the binary into the allocated buffer 193 | * we dump each segment and advance the buffer 194 | */ 195 | static void 196 | dump_binary(mach_vm_address_t address, void *buffer) 197 | { 198 | // allocate a buffer to read the header info 199 | // NOTE: this is not exactly correct since the 64bit version has an extra 4 bytes 200 | // but this will work for this purpose so no need for more complexity! 201 | struct mach_header header = {0}; 202 | readkmem(&header, address, sizeof(struct mach_header)); 203 | 204 | uint16_t mach_header_size = sizeof(struct mach_header); 205 | switch (header.magic) 206 | { 207 | case MH_MAGIC: 208 | { 209 | break; 210 | } 211 | case MH_MAGIC_64: 212 | { 213 | mach_header_size = sizeof(struct mach_header_64); 214 | break; 215 | } 216 | default: 217 | { 218 | ERROR_MSG("Target is not a mach-o binary!"); 219 | exit(-1); 220 | } 221 | } 222 | 223 | // read the header info to find the LINKEDIT 224 | uint8_t *loadcmds = malloc(header.sizeofcmds*sizeof(uint8_t)); 225 | if (loadcmds == NULL) 226 | { 227 | printf("[ERROR] Failed to allocate memory (%s).\n", __FUNCTION__); 228 | exit(-1); 229 | } 230 | // retrieve the load commands 231 | readkmem(loadcmds, address+mach_header_size, header.sizeofcmds); 232 | 233 | // process and retrieve address and size of linkedit 234 | uint8_t *loadCmdAddress = 0; 235 | // first load cmd address 236 | loadCmdAddress = (uint8_t*)loadcmds; 237 | struct load_command *loadCommand = NULL; 238 | struct segment_command *segCmd = NULL; 239 | struct segment_command_64 *segCmd64 = NULL; 240 | // process commands to find the info we need 241 | for (uint32_t i = 0; i < header.ncmds; i++) 242 | { 243 | loadCommand = (struct load_command*)loadCmdAddress; 244 | // 32bits and 64 bits segment commands 245 | if (loadCommand->cmd == LC_SEGMENT) 246 | { 247 | segCmd = (struct segment_command*)loadCmdAddress; 248 | if (strcmp((char*)(segCmd->segname), "__PAGEZERO") != 0) 249 | { 250 | #if DEBUG 251 | DEBUG_MSG("Dumping %s at 0x%llx with size 0x%x (buffer:%x)", segCmd->segname, segCmd->vmaddr+vmaddr_slide, segCmd->filesize, (uint32_t)buffer); 252 | #endif 253 | // sync buffer position with file offset 254 | buffer += segCmd->fileoff; 255 | // we don't need to dump header plus load cmds because __TEXT segment address includes them! 256 | readkmem(buffer, segCmd->vmaddr+vmaddr_slide, segCmd->filesize); 257 | } 258 | 259 | } 260 | else if (loadCommand->cmd == LC_SEGMENT_64) 261 | { 262 | segCmd64 = (struct segment_command_64*)loadCmdAddress; 263 | if (strcmp((char*)(segCmd64->segname), "__PAGEZERO") != 0) 264 | { 265 | #if DEBUG 266 | DEBUG_MSG("Dumping %s at 0x%llx with size 0x%llx (buffer:%p)", segCmd64->segname, segCmd64->vmaddr+vmaddr_slide, segCmd64->filesize, buffer); 267 | #endif 268 | // sync buffer position with file offset 269 | buffer += segCmd64->fileoff; 270 | // we don't need to dump header plus load cmds because __TEXT segment address includes them! 271 | readkmem(buffer, segCmd64->vmaddr+vmaddr_slide, (size_t)segCmd64->filesize); 272 | } 273 | } 274 | // advance to next command 275 | loadCmdAddress += loadCommand->cmdsize; 276 | } 277 | end: 278 | free(loadcmds); 279 | } 280 | 281 | // retrieve which kernel type are we running, 32 or 64 bits 282 | int 283 | get_kernel_type(void) 284 | { 285 | size_t size; 286 | if (sysctlbyname("hw.machine", NULL, &size, NULL, 0)) 287 | { 288 | ERROR_MSG("Failed to retrieve hw.machine size."); 289 | exit(-1); 290 | } 291 | char *machine = malloc(size); 292 | if (machine == NULL) 293 | { 294 | ERROR_MSG("Failed to allocate memory (%s)", __FUNCTION__); 295 | exit(-1); 296 | } 297 | 298 | if (sysctlbyname("hw.machine", machine, &size, NULL, 0)) 299 | { 300 | ERROR_MSG("Failed to retrieve hw.machine."); 301 | exit(-1); 302 | } 303 | 304 | int8_t ret = -1; 305 | if (strcmp(machine, "i386") == 0) 306 | { 307 | ret = x86; 308 | } 309 | else if (strcmp(machine, "x86_64") == 0) 310 | { 311 | ret = x64; 312 | } 313 | 314 | free(machine); 315 | return ret; 316 | } 317 | 318 | void 319 | readkmem(void *buffer, mach_vm_address_t target_addr, size_t size) 320 | { 321 | if (g_kmem_source.kernel_port != 0) 322 | { 323 | mach_vm_size_t outsize = 0; 324 | kern_return_t kr = mach_vm_read_overwrite(g_kmem_source.kernel_port, target_addr, size, (mach_vm_address_t)buffer, &outsize); 325 | if (kr != KERN_SUCCESS) 326 | { 327 | ERROR_MSG("mach_vm_read_overwrite failed!"); 328 | exit(-1); 329 | } 330 | } 331 | else if (g_kmem_source.fd != 0) 332 | { 333 | if(lseek(g_kmem_source.fd, target_addr, SEEK_SET) != (off_t)target_addr) 334 | { 335 | ERROR_MSG("Error in lseek. Are you root?"); 336 | exit(-1); 337 | } 338 | 339 | ssize_t bytes_read = read(g_kmem_source.fd, buffer, size); 340 | if(bytes_read != size) 341 | { 342 | ERROR_MSG("Error while trying to read from kmem. Asked %ld bytes from offset %llx, returned %ld.", size, target_addr, bytes_read); 343 | exit(-1); 344 | } 345 | } 346 | } 347 | 348 | void 349 | usage(void) 350 | { 351 | OUTPUT_MSG("readkmem -a address -s size [-o filename] [-f]"); 352 | OUTPUT_MSG("Available Options : "); 353 | OUTPUT_MSG(" -o filename file to write binary output to"); 354 | OUTPUT_MSG(" -f make a full dump of target binary"); 355 | exit(-1); 356 | } 357 | 358 | void 359 | header(void) 360 | { 361 | 362 | OUTPUT_MSG(" _____ _ _____"); 363 | OUTPUT_MSG("| __ |___ ___ _| | | |_____ ___ _____"); 364 | OUTPUT_MSG("| -| -_| .'| . | -| | -_| |"); 365 | OUTPUT_MSG("|__|__|___|__,|___|__|__|_|_|_|___|_|_|_|"); 366 | OUTPUT_MSG(" Readkmem v%s - (c) fG!",VERSION); 367 | OUTPUT_MSG("-----------------------------------------"); 368 | } 369 | 370 | int main(int argc, char ** argv) 371 | { 372 | 373 | // required structure for long options 374 | static struct option long_options[]={ 375 | { "address", required_argument, NULL, 'a' }, 376 | { "size", required_argument, NULL, 's' }, 377 | { "out", required_argument, NULL, 'o' }, 378 | { "full", no_argument, NULL, 'f' }, 379 | { NULL, 0, NULL, 0 } 380 | }; 381 | int option_index = 0; 382 | int c = 0; 383 | char *outputname = NULL; 384 | 385 | uint64_t address = 0; 386 | size_t size = 0; 387 | uint8_t fulldump = 0; 388 | 389 | // process command line options 390 | while ((c = getopt_long (argc, argv, "a:s:o:f", long_options, &option_index)) != -1) 391 | { 392 | switch (c) 393 | { 394 | case ':': 395 | usage(); 396 | exit(-1); 397 | break; 398 | case '?': 399 | usage(); 400 | exit(-1); 401 | break; 402 | case 'o': 403 | outputname = optarg; 404 | break; 405 | case 'a': 406 | address = strtoul(optarg, NULL, 0); 407 | break; 408 | case 's': 409 | size = strtoul(optarg, NULL, 0); 410 | break; 411 | case 'f': 412 | fulldump = 1; 413 | break; 414 | default: 415 | usage(); 416 | exit(-1); 417 | } 418 | } 419 | 420 | header(); 421 | 422 | if (argc < 3) 423 | { 424 | usage(); 425 | } 426 | 427 | // we need to run this as root 428 | if (getuid() != 0) 429 | { 430 | ERROR_MSG("Please run me as root!"); 431 | exit(-1); 432 | } 433 | 434 | if (size == 0) 435 | { 436 | ERROR_MSG("Size is zero!"); 437 | exit(-1); 438 | } 439 | 440 | int kernel_type = get_kernel_type(); 441 | if (kernel_type == -1) 442 | { 443 | ERROR_MSG("Unable to retrieve kernel type!"); 444 | exit(-1); 445 | } 446 | 447 | /* test if we can read kernel memory using processor_set_tasks() vulnerability */ 448 | /* vulnerability presented at BlackHat Asia 2014 by Ming-chieh Pan, Sung-ting Tsai. */ 449 | /* also described in Mac OS X and iOS Internals, page 387 */ 450 | host_t host_port = mach_host_self(); 451 | mach_port_t proc_set_default = 0; 452 | mach_port_t proc_set_default_control = 0; 453 | task_array_t all_tasks = NULL; 454 | mach_msg_type_number_t all_tasks_cnt = 0; 455 | kern_return_t kr = 0; 456 | int valid_kernel_port = 0; 457 | 458 | kr = processor_set_default(host_port, &proc_set_default); 459 | if (kr == KERN_SUCCESS) 460 | { 461 | kr = host_processor_set_priv(host_port, proc_set_default, &proc_set_default_control); 462 | if (kr == KERN_SUCCESS) 463 | { 464 | kr = processor_set_tasks(proc_set_default_control, &all_tasks, &all_tasks_cnt); 465 | if (kr == KERN_SUCCESS) 466 | { 467 | OUTPUT_MSG("Found valid kernel port using processor_set_tasks() vulnerability!"); 468 | g_kmem_source.kernel_port = all_tasks[0]; 469 | valid_kernel_port = 1; 470 | } 471 | } 472 | } 473 | /* kernel not vulnerable, try to use /dev/kmem */ 474 | if (valid_kernel_port == 0) 475 | { 476 | if((g_kmem_source.fd = open("/dev/kmem",O_RDWR)) == -1) 477 | { 478 | ERROR_MSG("Error while opening /dev/kmem. Is /dev/kmem enabled?"); 479 | ERROR_MSG("Add parameter kmem=1 to /Library/Preferences/SystemConfiguration/com.apple.Boot.plist."); 480 | exit(-1); 481 | } 482 | } 483 | 484 | uint8_t *read_buffer = NULL; 485 | 486 | FILE *outputfile; 487 | if (outputname != NULL) 488 | { 489 | if ( (outputfile = fopen(outputname, "wb")) == NULL) 490 | { 491 | ERROR_MSG("Cannot open %s for output!", outputname); 492 | exit(-1); 493 | } 494 | } 495 | 496 | if (fulldump) 497 | { 498 | // first we need to find the file size because memory alignment slack spaces 499 | size_t imagesize = 0; 500 | imagesize = get_image_size(address); 501 | if (imagesize == 0) 502 | { 503 | ERROR_MSG("Invalid size!"); 504 | exit(-1); 505 | } 506 | DEBUG_MSG("Target image size is 0x%lx", imagesize); 507 | read_buffer = calloc(1, imagesize); 508 | // and finally read the sections and dump their contents to the buffer 509 | dump_binary(address, (void*)read_buffer); 510 | // dump buffer contents to file 511 | if (outputname != NULL) 512 | { 513 | if (fwrite(read_buffer, (long)imagesize, 1, outputfile) < 1) 514 | { 515 | ERROR_MSG("Write error at %s occurred!", outputname); 516 | exit(-1); 517 | } 518 | OUTPUT_MSG("\n[OK] Full binary dumped to %s!\n", outputname); 519 | } 520 | } 521 | else 522 | { 523 | read_buffer = calloc(1, size); 524 | if (read_buffer == NULL) 525 | { 526 | ERROR_MSG("Memory allocation failed (%s).", __FUNCTION__); 527 | exit(-1); 528 | } 529 | // read kernel memory 530 | readkmem(read_buffer, address, size); 531 | // dump to file 532 | if (outputname != NULL) 533 | { 534 | if (fwrite(read_buffer, size, 1, outputfile) < 1) 535 | { 536 | ERROR_MSG("Write error at %s occurred!", outputname); 537 | exit(-1); 538 | } 539 | OUTPUT_MSG("\n[OK] Memory dumped to %s!\n", outputname); 540 | } 541 | // dump to stdout 542 | else 543 | { 544 | int i = 0; 545 | int x = 0; 546 | int z = 0; 547 | size_t linelength = 0; 548 | printf("Memory hex dump @ %p:\n\n", (void*)address); 549 | // 16 columns 550 | while (i < size) 551 | { 552 | linelength = (size - i) <= 16 ? (size - i) : 16; 553 | printf("%p ",(void*)address); 554 | z = i; 555 | for (x = 0; x < linelength; x++, z++) 556 | { 557 | printf("%02x ", read_buffer[z]); 558 | } 559 | // make it always 16 columns, this could be prettier :P 560 | for (x = (int)linelength; x < 16; x++) 561 | { 562 | fprintf(stdout, " "); 563 | } 564 | z = i; 565 | printf("|"); 566 | for (x = 0; x < linelength; x++, z++) 567 | { 568 | printf("%c", isascii(read_buffer[z]) && isprint(read_buffer[z]) ? read_buffer[z] : '.'); 569 | } 570 | i += 16; 571 | printf("|\n"); 572 | address += 16; 573 | } 574 | printf("\n"); 575 | } 576 | } 577 | 578 | end: 579 | free(read_buffer); 580 | return 0; 581 | } 582 | --------------------------------------------------------------------------------