├── README.md ├── readphysmem.xcodeproj └── project.pbxproj └── readphysmem └── main.m /README.md: -------------------------------------------------------------------------------- 1 | readphysmem 2 | 3 | (c) fG! - 2015 - reverser@put.as - https://reverse.put.as 4 | 5 | A small utility to read and write to Macs physical memory using default AppleHWAccess.kext. 6 | 7 | This kext is loaded by default on Mavericks and Yosemite. 8 | It has (finally) been disabled on El Capitan since beta 7 release, since it was a obvious way to bypass and disable the new rootless protection ;-) 9 | 10 | Trammell Hudson wrote a similar utility using DirectHW.kext (also blacklisted on El Capitan B7). 11 | Available at https://github.com/osresearch/rwmem. 12 | 13 | The same warning as rwmem applies here. Use with caution, it can easily kernel panic your machine both on reads and writes (particularly on devices mapped areas, SMM ram, etc). If you already know PCI BAR addresses you need to use 4 bytes read size instead of default 8. 14 | 15 | It works great to read kernel and other memory, and also BIOS (since it's mapped/shadowed in physical memory). See also https://github.com/gdbinit/diagnostic_service2 for a real world rootkit application. 16 | 17 | DirectHW.kext is a bit more powerful since it allows to read port info. AppleHWAccess.kext only implements memory reads and not ports. For example, it can't be used to read PCI configuration. 18 | 19 | Have fun, 20 | 21 | fG! -------------------------------------------------------------------------------- /readphysmem.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7B676A0819D5BF24002C3287 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B676A0719D5BF24002C3287 /* main.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 7B676A0219D5BF24002C3287 /* 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 | 7B676A0419D5BF24002C3287 /* readphysmem */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = readphysmem; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 7B676A0719D5BF24002C3287 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 28 | 7BAD01501B98967B00A7F6FE /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 7B676A0119D5BF24002C3287 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 7B6769FB19D5BF24002C3287 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 7BAD01501B98967B00A7F6FE /* README.md */, 46 | 7B676A0619D5BF24002C3287 /* readphysmem */, 47 | 7B676A0519D5BF24002C3287 /* Products */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 7B676A0519D5BF24002C3287 /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 7B676A0419D5BF24002C3287 /* readphysmem */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | 7B676A0619D5BF24002C3287 /* readphysmem */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 7B676A0719D5BF24002C3287 /* main.m */, 63 | ); 64 | path = readphysmem; 65 | sourceTree = ""; 66 | }; 67 | /* End PBXGroup section */ 68 | 69 | /* Begin PBXNativeTarget section */ 70 | 7B676A0319D5BF24002C3287 /* readphysmem */ = { 71 | isa = PBXNativeTarget; 72 | buildConfigurationList = 7B676A0B19D5BF24002C3287 /* Build configuration list for PBXNativeTarget "readphysmem" */; 73 | buildPhases = ( 74 | 7B676A0019D5BF24002C3287 /* Sources */, 75 | 7B676A0119D5BF24002C3287 /* Frameworks */, 76 | 7B676A0219D5BF24002C3287 /* CopyFiles */, 77 | ); 78 | buildRules = ( 79 | ); 80 | dependencies = ( 81 | ); 82 | name = readphysmem; 83 | productName = readphysmem; 84 | productReference = 7B676A0419D5BF24002C3287 /* readphysmem */; 85 | productType = "com.apple.product-type.tool"; 86 | }; 87 | /* End PBXNativeTarget section */ 88 | 89 | /* Begin PBXProject section */ 90 | 7B6769FC19D5BF24002C3287 /* Project object */ = { 91 | isa = PBXProject; 92 | attributes = { 93 | LastUpgradeCheck = 0600; 94 | ORGANIZATIONNAME = Put.as; 95 | TargetAttributes = { 96 | 7B676A0319D5BF24002C3287 = { 97 | CreatedOnToolsVersion = 6.0.1; 98 | }; 99 | }; 100 | }; 101 | buildConfigurationList = 7B6769FF19D5BF24002C3287 /* Build configuration list for PBXProject "readphysmem" */; 102 | compatibilityVersion = "Xcode 3.2"; 103 | developmentRegion = English; 104 | hasScannedForEncodings = 0; 105 | knownRegions = ( 106 | en, 107 | ); 108 | mainGroup = 7B6769FB19D5BF24002C3287; 109 | productRefGroup = 7B676A0519D5BF24002C3287 /* Products */; 110 | projectDirPath = ""; 111 | projectRoot = ""; 112 | targets = ( 113 | 7B676A0319D5BF24002C3287 /* readphysmem */, 114 | ); 115 | }; 116 | /* End PBXProject section */ 117 | 118 | /* Begin PBXSourcesBuildPhase section */ 119 | 7B676A0019D5BF24002C3287 /* Sources */ = { 120 | isa = PBXSourcesBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | 7B676A0819D5BF24002C3287 /* main.m in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | 7B676A0919D5BF24002C3287 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 135 | CLANG_CXX_LIBRARY = "libc++"; 136 | CLANG_ENABLE_MODULES = YES; 137 | CLANG_ENABLE_OBJC_ARC = YES; 138 | CLANG_WARN_BOOL_CONVERSION = YES; 139 | CLANG_WARN_CONSTANT_CONVERSION = YES; 140 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 141 | CLANG_WARN_EMPTY_BODY = YES; 142 | CLANG_WARN_ENUM_CONVERSION = YES; 143 | CLANG_WARN_INT_CONVERSION = YES; 144 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 145 | CLANG_WARN_UNREACHABLE_CODE = YES; 146 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 147 | COPY_PHASE_STRIP = NO; 148 | ENABLE_STRICT_OBJC_MSGSEND = YES; 149 | GCC_C_LANGUAGE_STANDARD = gnu99; 150 | GCC_DYNAMIC_NO_PIC = NO; 151 | GCC_OPTIMIZATION_LEVEL = 0; 152 | GCC_PREPROCESSOR_DEFINITIONS = ( 153 | "DEBUG=1", 154 | "$(inherited)", 155 | ); 156 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 157 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 158 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 159 | GCC_WARN_UNDECLARED_SELECTOR = YES; 160 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 161 | GCC_WARN_UNUSED_FUNCTION = YES; 162 | GCC_WARN_UNUSED_VARIABLE = YES; 163 | MACOSX_DEPLOYMENT_TARGET = 10.9; 164 | MTL_ENABLE_DEBUG_INFO = YES; 165 | ONLY_ACTIVE_ARCH = YES; 166 | SDKROOT = macosx; 167 | }; 168 | name = Debug; 169 | }; 170 | 7B676A0A19D5BF24002C3287 /* Release */ = { 171 | isa = XCBuildConfiguration; 172 | buildSettings = { 173 | ALWAYS_SEARCH_USER_PATHS = NO; 174 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 175 | CLANG_CXX_LIBRARY = "libc++"; 176 | CLANG_ENABLE_MODULES = YES; 177 | CLANG_ENABLE_OBJC_ARC = YES; 178 | CLANG_WARN_BOOL_CONVERSION = YES; 179 | CLANG_WARN_CONSTANT_CONVERSION = YES; 180 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 181 | CLANG_WARN_EMPTY_BODY = YES; 182 | CLANG_WARN_ENUM_CONVERSION = YES; 183 | CLANG_WARN_INT_CONVERSION = YES; 184 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 185 | CLANG_WARN_UNREACHABLE_CODE = YES; 186 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 187 | COPY_PHASE_STRIP = YES; 188 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 189 | ENABLE_NS_ASSERTIONS = NO; 190 | ENABLE_STRICT_OBJC_MSGSEND = YES; 191 | GCC_C_LANGUAGE_STANDARD = gnu99; 192 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 193 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 194 | GCC_WARN_UNDECLARED_SELECTOR = YES; 195 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 196 | GCC_WARN_UNUSED_FUNCTION = YES; 197 | GCC_WARN_UNUSED_VARIABLE = YES; 198 | MACOSX_DEPLOYMENT_TARGET = 10.9; 199 | MTL_ENABLE_DEBUG_INFO = NO; 200 | SDKROOT = macosx; 201 | }; 202 | name = Release; 203 | }; 204 | 7B676A0C19D5BF24002C3287 /* Debug */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | PRODUCT_NAME = "$(TARGET_NAME)"; 208 | }; 209 | name = Debug; 210 | }; 211 | 7B676A0D19D5BF24002C3287 /* Release */ = { 212 | isa = XCBuildConfiguration; 213 | buildSettings = { 214 | PRODUCT_NAME = "$(TARGET_NAME)"; 215 | }; 216 | name = Release; 217 | }; 218 | /* End XCBuildConfiguration section */ 219 | 220 | /* Begin XCConfigurationList section */ 221 | 7B6769FF19D5BF24002C3287 /* Build configuration list for PBXProject "readphysmem" */ = { 222 | isa = XCConfigurationList; 223 | buildConfigurations = ( 224 | 7B676A0919D5BF24002C3287 /* Debug */, 225 | 7B676A0A19D5BF24002C3287 /* Release */, 226 | ); 227 | defaultConfigurationIsVisible = 0; 228 | defaultConfigurationName = Release; 229 | }; 230 | 7B676A0B19D5BF24002C3287 /* Build configuration list for PBXNativeTarget "readphysmem" */ = { 231 | isa = XCConfigurationList; 232 | buildConfigurations = ( 233 | 7B676A0C19D5BF24002C3287 /* Debug */, 234 | 7B676A0D19D5BF24002C3287 /* Release */, 235 | ); 236 | defaultConfigurationIsVisible = 0; 237 | defaultConfigurationName = Release; 238 | }; 239 | /* End XCConfigurationList section */ 240 | }; 241 | rootObject = 7B6769FC19D5BF24002C3287 /* Project object */; 242 | } 243 | -------------------------------------------------------------------------------- /readphysmem/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * _____ _ _____ _ _____ 4 | * | __ |___ ___ _| | _ | |_ _ _ ___| |___ _____ 5 | * | -| -_| .'| . | __| | | |_ -| | | | -_| | 6 | * |__|__|___|__,|___|__| |_|_|_ |___|_|_|_|___|_|_|_| 7 | * |___| 8 | * 9 | * Created by fG! on 27/08/2015. 10 | * Copyright (c) 2015 fG!. All rights reserved. 11 | * 12 | * A small utility to read and write physical memory using AppleHWAccess.kext 13 | * available since Mavericks 14 | * 15 | * Similar to rwmem by Trammell Hudson (https://github.com/osresearch/rwmem) 16 | * This one uses DirectHW.kext instead 17 | * 18 | * main.m 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define VERSION "0.1" 31 | 32 | #define ERROR_MSG(fmt, ...) fprintf(stderr, "[ERROR] " fmt " \n", ## __VA_ARGS__) 33 | #define OUTPUT_MSG(fmt, ...) fprintf(stdout, fmt " \n", ## __VA_ARGS__) 34 | #if DEBUG == 0 35 | # define DEBUG_MSG(fmt, ...) do {} while (0) 36 | #else 37 | # define DEBUG_MSG(fmt, ...) fprintf(stdout, "[DEBUG] " fmt "\n", ## __VA_ARGS__) 38 | #endif 39 | 40 | /* stuff we need to use AppleHWAccess */ 41 | #define kAppleHWAccessClass "AppleHWAccess" 42 | #define kAppleHWRead 0 43 | #define kAppleHWWrite 1 44 | 45 | struct __attribute__ ((packed)) HWRequest 46 | { 47 | uint32_t width; 48 | uint64_t offset; 49 | uint64_t data; 50 | }; 51 | 52 | static void 53 | usage(void) 54 | { 55 | OUTPUT_MSG("---[ Usage ]---"); 56 | OUTPUT_MSG("readphysmem -a address -s size [-b read_size] [-o filename]"); 57 | OUTPUT_MSG("\nAvailable Options : "); 58 | OUTPUT_MSG(" -o filename file to write binary output to"); 59 | OUTPUT_MSG(" -b 1/2/4/8 read size, default is 8 bytes."); 60 | OUTPUT_MSG(" note: PCI areas for example must use 4 bytes"); 61 | OUTPUT_MSG("\nDefault output is hexdump if no output file specified."); 62 | exit(-1); 63 | } 64 | 65 | static void 66 | header(void) 67 | { 68 | OUTPUT_MSG(" _____ _ _____ _ _____ "); 69 | OUTPUT_MSG(" | __ |___ ___ _| | _ | |_ _ _ ___| |___ _____ "); 70 | OUTPUT_MSG(" | -| -_| .'| . | __| | | |_ -| | | | -_| |"); 71 | OUTPUT_MSG(" |__|__|___|__,|___|__| |_|_|_ |___|_|_|_|___|_|_|_|"); 72 | OUTPUT_MSG(" |___| "); 73 | OUTPUT_MSG(" ReadPhysMem v%s - (c) fG!",VERSION); 74 | OUTPUT_MSG("-------------------------------------------------------------"); 75 | OUTPUT_MSG("A OS X physical memory reader/writer using AppleHWAccess.kext"); 76 | OUTPUT_MSG("-------------------------------------------------------------"); 77 | OUTPUT_MSG(""); 78 | } 79 | 80 | static kern_return_t 81 | read_physical_mem(uint64_t address, uint64_t length, uint64_t read_size, uint8_t *out_data) 82 | { 83 | kern_return_t kr = 0; 84 | 85 | uint64_t avail_mem = 0; 86 | size_t len = sizeof(avail_mem); 87 | if ( sysctlbyname("hw.memsize", &avail_mem, &len, NULL, 0) != 0 ) 88 | { 89 | ERROR_MSG("Failed to retrieve available memory."); 90 | return KERN_FAILURE; 91 | } 92 | 93 | if (address + length > avail_mem) 94 | { 95 | ERROR_MSG("Requested address is out of available memory bounds."); 96 | return KERN_FAILURE; 97 | } 98 | 99 | io_service_t service = MACH_PORT_NULL; 100 | /* open connection to the kernel extension */ 101 | service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kAppleHWAccessClass)); 102 | if (!service) 103 | { 104 | ERROR_MSG("Can't find AppleHWAccess service."); 105 | return KERN_FAILURE; 106 | } 107 | 108 | io_connect_t connect = MACH_PORT_NULL; 109 | kr = IOServiceOpen(service, mach_task_self(), 0, &connect); 110 | if (kr != KERN_SUCCESS) 111 | { 112 | ERROR_MSG("Failed to open AppleHWAccess IOService."); 113 | IOObjectRelease(service); 114 | return KERN_FAILURE; 115 | } 116 | 117 | /* Trammell on his rwmem does 4 bytes at a time 118 | * doing 8 here seems to work and it's 50% faster than doing it 4 bytes 119 | * at a time 120 | * At least reading the BIOS area contents doesn't give problems 121 | */ 122 | 123 | /* XXX: to read from PCI region we need to copy 4 bytes at a time 124 | * check rwmem warning 125 | * with 8 bytes with get everything FF 126 | * but it works with 4 bytes as written by Trammell 127 | */ 128 | /* XXX: we should also align our reads else this can go wrong on last bytes for PCI region cases */ 129 | uint64_t quotient = length / read_size; 130 | uint64_t remainder = length % read_size; 131 | 132 | uint32_t in_size = (uint32_t)read_size * 8; 133 | struct HWRequest req_in = {in_size, address}; 134 | struct HWRequest req_out = {0}; 135 | 136 | size_t req_size = sizeof(struct HWRequest); 137 | 138 | while (req_in.offset < address + length - remainder) 139 | { 140 | /* selector = 0 for read */ 141 | if ( (kr = IOConnectCallStructMethod(connect, kAppleHWRead, &req_in, req_size, &req_out, &req_size)) != KERN_SUCCESS) 142 | { 143 | ERROR_MSG("IOConnectCallStructMethod failed on read: %x", kr); 144 | break; 145 | } 146 | memcpy(out_data, &req_out.data, read_size); 147 | req_in.offset += read_size; 148 | out_data += read_size; 149 | } 150 | /* read any remaining bytes 1 by 1 */ 151 | if (remainder > 0) 152 | { 153 | /* read one byte at a time */ 154 | read_size = 1; 155 | req_in.width = (uint32_t)read_size * 8; 156 | 157 | while (req_in.offset < address + length) 158 | { 159 | /* selector = 0 for read */ 160 | if ( (kr = IOConnectCallStructMethod(connect, kAppleHWRead, &req_in, req_size, &req_out, &req_size)) != KERN_SUCCESS) 161 | { 162 | ERROR_MSG("IOConnectCallStructMethod failed on read: %x", kr); 163 | break; 164 | } 165 | memcpy(out_data, &req_out.data, read_size); 166 | req_in.offset += read_size; 167 | out_data += read_size; 168 | } 169 | } 170 | 171 | IOServiceClose(connect); 172 | IOObjectRelease(connect); 173 | IOObjectRelease(service); 174 | return KERN_SUCCESS; 175 | } 176 | 177 | static kern_return_t 178 | write_physical_mem(uint64_t address, uint64_t length, uint64_t write_size, uint8_t *in_data) 179 | { 180 | kern_return_t kr = 0; 181 | 182 | uint64_t avail_mem = 0; 183 | size_t len = sizeof(avail_mem); 184 | if ( sysctlbyname("hw.memsize", &avail_mem, &len, NULL, 0) != 0 ) 185 | { 186 | ERROR_MSG("Failed to retrieve available memory."); 187 | return KERN_FAILURE; 188 | } 189 | 190 | if (address + length > avail_mem) 191 | { 192 | ERROR_MSG("Requested address is out of available memory bounds."); 193 | return KERN_FAILURE; 194 | } 195 | 196 | io_service_t service = MACH_PORT_NULL; 197 | /* open connection to the kernel extension */ 198 | service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kAppleHWAccessClass)); 199 | if (!service) 200 | { 201 | ERROR_MSG("Can't find AppleHWAccess service."); 202 | return KERN_FAILURE; 203 | } 204 | 205 | io_connect_t connect = MACH_PORT_NULL; 206 | kr = IOServiceOpen(service, mach_task_self(), 0, &connect); 207 | if (kr != KERN_SUCCESS) 208 | { 209 | ERROR_MSG("Failed to open AppleHWAccess IOService."); 210 | IOObjectRelease(service); 211 | return KERN_FAILURE; 212 | } 213 | 214 | /* XXX: to write from PCI region we need to copy 4 bytes at a time ???? */ 215 | /* XXX: we should also align our reads else this can go wrong on last bytes for PCI region cases */ 216 | uint64_t quotient = length / write_size; 217 | uint64_t remainder = length % write_size; 218 | 219 | uint32_t in_size = (uint32_t)write_size * 8; 220 | struct HWRequest req_in = {in_size, address}; 221 | struct HWRequest req_out = {0}; 222 | 223 | size_t req_size = sizeof(struct HWRequest); 224 | 225 | uint8_t *data_to_write = in_data; 226 | 227 | while (req_in.offset < address + length - remainder) 228 | { 229 | /* copy data to request structure */ 230 | memcpy((void*)&req_in.data, data_to_write, write_size); 231 | if ( (kr = IOConnectCallStructMethod(connect, kAppleHWWrite, &req_in, req_size, &req_out, &req_size)) != KERN_SUCCESS) 232 | { 233 | ERROR_MSG("IOConnectCallStructMethod failed on write: %x", kr); 234 | break; 235 | } 236 | /* advance data */ 237 | req_in.offset += write_size; 238 | data_to_write += write_size; 239 | } 240 | /* read any remaining bytes 1 by 1 */ 241 | if (remainder > 0) 242 | { 243 | /* read one byte at a time */ 244 | write_size = 1; 245 | req_in.width = (uint32_t)write_size * 8; 246 | 247 | while (req_in.offset < address + length) 248 | { 249 | /* copy data to request structure */ 250 | memcpy((void*)&req_in.data, data_to_write, write_size); 251 | if ( (kr = IOConnectCallStructMethod(connect, kAppleHWWrite, &req_in, req_size, &req_out, &req_size)) != KERN_SUCCESS) 252 | { 253 | ERROR_MSG("IOConnectCallStructMethod failed on write: %x", kr); 254 | break; 255 | } 256 | req_in.offset += write_size; 257 | data_to_write += write_size; 258 | } 259 | } 260 | 261 | IOServiceClose(connect); 262 | IOObjectRelease(connect); 263 | IOObjectRelease(service); 264 | return KERN_SUCCESS; 265 | } 266 | 267 | int 268 | main(int argc, char * argv[]) 269 | { 270 | @autoreleasepool { 271 | 272 | header(); 273 | 274 | static struct option long_options[]={ 275 | { "address", required_argument, NULL, 'a' }, 276 | { "size", required_argument, NULL, 's' }, 277 | { "out", required_argument, NULL, 'o' }, 278 | { "readsize", required_argument, NULL, 'b' }, 279 | { "in", required_argument, NULL, 'i' }, 280 | { "write", no_argument, NULL, 'w' }, 281 | { NULL, 0, NULL, 0 } 282 | }; 283 | 284 | int option_index = 0; 285 | int c = 0; 286 | char *outputname = NULL; 287 | char *input_name = NULL; 288 | 289 | uint64_t address = 0; 290 | size_t size = 0; 291 | /* default is 8 bytes */ 292 | uint64_t read_size = 8; 293 | uint64_t do_write = 0; 294 | 295 | /* process command line options */ 296 | while ((c = getopt_long(argc, argv, "a:s:o:b:i:wh", long_options, &option_index)) != -1) 297 | { 298 | switch (c) 299 | { 300 | case 'h': 301 | usage(); 302 | return EXIT_FAILURE; 303 | case 'o': 304 | outputname = optarg; 305 | break; 306 | case 'i': 307 | input_name = optarg; 308 | break; 309 | case 'a': 310 | address = strtoul(optarg, NULL, 0); 311 | break; 312 | case 's': 313 | size = strtoul(optarg, NULL, 0); 314 | break; 315 | case 'b': 316 | read_size = strtoull(optarg, NULL, 0); 317 | break; 318 | case 'w': 319 | do_write = 1; 320 | break; 321 | default: 322 | usage(); 323 | return EXIT_FAILURE; 324 | } 325 | } 326 | 327 | if (argc < 3) 328 | { 329 | usage(); 330 | } 331 | 332 | /* we need to run this as root */ 333 | if (getuid() != 0) 334 | { 335 | ERROR_MSG("Must be run as root to talk to AppleHWAccess.kext!"); 336 | return EXIT_FAILURE; 337 | } 338 | 339 | if (size == 0) 340 | { 341 | ERROR_MSG("Size is zero. That doesn't make any sense, does it?"); 342 | return EXIT_FAILURE; 343 | } 344 | 345 | switch (read_size) { 346 | case 1: 347 | case 2: 348 | case 4: 349 | case 8: 350 | break; 351 | default: 352 | ERROR_MSG("Invalid read size, must be 1,2,4,8 bytes."); 353 | return EXIT_FAILURE; 354 | } 355 | 356 | /* retrive amount of physical memory */ 357 | uint64_t installed_mem = 0; 358 | size_t len = sizeof(installed_mem); 359 | if (sysctlbyname("hw.memsize", &installed_mem, &len, NULL, 0) != 0) 360 | { 361 | ERROR_MSG("Failed to retrieve available memory."); 362 | return EXIT_FAILURE; 363 | } 364 | /* are we trying to read out of bounds? */ 365 | if (address + size > installed_mem) 366 | { 367 | ERROR_MSG("Requested address is out of available memory bounds."); 368 | return EXIT_FAILURE; 369 | } 370 | 371 | /* XXX: check out requests that we know will lead to crashes? */ 372 | 373 | if (do_write == 1) 374 | { 375 | if (input_name == NULL) 376 | { 377 | ERROR_MSG("No input file to write!"); 378 | return EXIT_FAILURE; 379 | } 380 | OUTPUT_MSG("Trying to write contents of %s into physical address 0x%llx...", input_name, address); 381 | int fd = -1; 382 | fd = open(input_name, O_RDONLY); 383 | if (fd < 0) 384 | { 385 | ERROR_MSG("Cannot open %s for input.", input_name); 386 | return EXIT_FAILURE; 387 | } 388 | uint8_t *input_buf = NULL; 389 | struct stat statbuf = {0}; 390 | if (fstat(fd, &statbuf) < 0) 391 | { 392 | ERROR_MSG("Can't fstat %s: %s.", input_name, strerror(errno)); 393 | close(fd); 394 | return EXIT_FAILURE; 395 | } 396 | if ((input_buf = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) 397 | { 398 | ERROR_MSG("Mmap failed on %s: %s", input_name, strerror(errno)); 399 | close(fd); 400 | return KERN_FAILURE; 401 | } 402 | close(fd); 403 | if (write_physical_mem(address, size, read_size, input_buf) != KERN_SUCCESS) 404 | { 405 | ERROR_MSG("Failed to write to physical memory."); 406 | munmap(input_buf, statbuf.st_size); 407 | return EXIT_FAILURE; 408 | } 409 | OUTPUT_MSG("All done! Writing (should have been) successful."); 410 | } 411 | else 412 | { 413 | OUTPUT_MSG("Trying to read contents of physical address 0x%llx...", address); 414 | uint8_t *data_buffer = calloc(1, size); 415 | if (data_buffer == NULL) 416 | { 417 | ERROR_MSG("Failed to allocate output buffer."); 418 | return EXIT_FAILURE; 419 | } 420 | 421 | if (read_physical_mem(address, size, read_size, data_buffer) != KERN_SUCCESS) 422 | { 423 | ERROR_MSG("Failed to read physical memory."); 424 | free(data_buffer); 425 | return EXIT_FAILURE; 426 | } 427 | 428 | /* finally, write to filename or dump to the screen */ 429 | if (outputname != NULL) 430 | { 431 | FILE *outputfile = NULL; 432 | if ( (outputfile = fopen(outputname, "wb")) == NULL) 433 | { 434 | ERROR_MSG("Cannot open %s for output!", outputname); 435 | return EXIT_FAILURE; 436 | } 437 | fwrite(data_buffer, size, 1, outputfile); 438 | fclose(outputfile); 439 | } 440 | else 441 | { 442 | OUTPUT_MSG("Memory hex dump @ 0x%llx:\n", address); 443 | 444 | uint64_t row_address = address; 445 | /* how many 16 columns rows? */ 446 | uint64_t rows = size / 16; 447 | uint64_t last_row = size % 16; 448 | /* hold the position to print inside the buffer */ 449 | size_t cur_pos = 0; 450 | /* print all 16 bytes rows */ 451 | for (uint64_t y = 0; y < rows; y++) 452 | { 453 | printf("0x%llx ",row_address); 454 | for (int x = 0; x < 16; x++, cur_pos++) 455 | { 456 | printf("%02x ", data_buffer[cur_pos]); 457 | } 458 | cur_pos -= 16; 459 | printf("|"); 460 | for (int x = 0; x < 16; x++, cur_pos++) 461 | { 462 | printf("%c", isascii(data_buffer[cur_pos]) && isprint(data_buffer[cur_pos]) ? data_buffer[cur_pos] : '.'); 463 | } 464 | printf("|\n"); 465 | row_address += 16; 466 | } 467 | /* whatever is left that is less than a 16 bytes column */ 468 | if (last_row > 0) 469 | { 470 | printf("0x%llx ",row_address); 471 | for (uint64_t x = 0; x < last_row; x++, cur_pos++) 472 | { 473 | printf("%02x ", data_buffer[cur_pos]); 474 | } 475 | /* make it aligned to 16 byte column */ 476 | for (uint64_t x = last_row; x < 16; x++) 477 | { 478 | fprintf(stdout, " "); 479 | } 480 | cur_pos -= last_row; 481 | printf("|"); 482 | for (int x = 0; x < 16; x++, cur_pos++) 483 | { 484 | printf("%c", isascii(data_buffer[cur_pos]) && isprint(data_buffer[cur_pos]) ? data_buffer[cur_pos] : '.'); 485 | } 486 | printf("|\n"); 487 | } 488 | printf("\n"); 489 | } 490 | free(data_buffer); 491 | OUTPUT_MSG("All done..."); 492 | } 493 | } 494 | 495 | return EXIT_SUCCESS; 496 | } 497 | --------------------------------------------------------------------------------