#!/usr/sbin/dtrace -q -s /* * Demonstration D script for watching Code Signing activity in the system * * As presented, this script will record and report all Code Signing activity * in one process (argument=pid), or all processes (argument='*'). * You are encouraged to modify it as you will. (A good start is to comment out * the print statements you don't like to see.) */ typedef uint64_t DTHandle; /* generic API handle (NOT a pointer) */ typedef uint8_t Hash[20]; /* SHA-1 */ typedef struct { /* from implementation */ uint32_t cputype; uint32_t cpusubtype; off_t offset; uint8_t fileOnly; } DiskRepContext; /* * Local variables used for suitable casting (only) */ self uint8_t *hash; /* * Startup (this may take a while) */ :::BEGIN { printf("Ready...\n"); } /* * Finishing (add statistics tracers here) */ :::END { } /* * Track kernel-related objects. * Each process has their own, and they're usually created very early. */ struct { DTHandle rep; /* DiskRep */ DTHandle staticCode; /* static code */ DTHandle code; /* dynamic code */ } kernel[pid_t]; /* * Track DiskRep objects. * DiskReps are drivers for on-disk formats. Beyond their natural concerns, * they also carry the path information for StaticCode objects. */ typedef struct { DTHandle me; /* own handle, if valid */ string path; /* canonical path */ string type; /* type string */ DiskRepContext ctx; /* construction context, if any */ DTHandle sub; /* sub-DiskRep if any */ } DiskRep; DiskRep rep[DTHandle]; /* all the DiskReps we've seen */ self uint64_t ctx; /* passes construction context, NULL if none */ codesign$1:::diskrep-create-* /* preset none */ { self->ctx = 0; } codesign$1:::diskrep-create-kernel { rep[arg0].me = kernel[pid].rep = arg0; rep[arg0].path = "(kernel)"; rep[arg0].type = "kernel"; printf("%8u %s[%d]%s(%p,KERNEL)\n", timestamp, execname, pid, probename, arg0); } codesign$1:::diskrep-create-macho { rep[arg0].me = arg0; rep[arg0].path = copyinstr(arg1); rep[arg0].type = "macho"; self->ctx = arg2; printf("%8u %s[%d]%s(%p,%s)\n", timestamp, execname, pid, probename, arg0, rep[arg0].path); } codesign$1:::diskrep-create-bundle-path { rep[arg0].me = arg0; rep[arg0].path = copyinstr(arg1); rep[arg0].type = "bundle"; self->ctx = arg2; rep[arg0].sub = arg3; printf("%8u %s[%d]%s(%p,%s,%p)\n", timestamp, execname, pid, probename, arg0, rep[arg0].path, rep[arg0].sub); } codesign$1:::diskrep-create-bundle-ref { rep[arg0].me = arg0; rep[arg0].path = "(from ref)"; rep[arg0].type = "bundle"; self->ctx = arg2; rep[arg0].sub = arg3; printf("%8u %s[%d]%s(%p,%s,%p)\n", timestamp, execname, pid, probename, arg0, rep[arg0].path, rep[arg0].sub); } codesign$1:::diskrep-create-file { rep[arg0].me = arg0; rep[arg0].path = copyinstr(arg1); rep[arg0].type = "file"; printf("%8u %s[%d]%s(%p,%s)\n", timestamp, execname, pid, probename, arg0, rep[arg0].path); } self DiskRepContext *ctxp; codesign$1:::diskrep-create-* / self->ctx / { self->ctxp = (DiskRepContext *)copyin(self->ctx, sizeof(DiskRepContext)); rep[arg0].ctx = *self->ctxp; printf("%8u %s[%d] ...context: arch=(0x%x,0x%x) offset=0x%x file=%d\n", timestamp, execname, pid, self->ctxp->cputype, self->ctxp->cpusubtype, self->ctxp->offset, self->ctxp->fileOnly); } codesign$1:::diskrep-destroy { printf("%8u %s[%d]%s(%p,%s)\n", timestamp, execname, pid, probename, arg0, rep[arg0].path); rep[arg0].me = 0; } /* * Track Code Signing API objects */ typedef struct { DTHandle me; DTHandle host; DTHandle staticCode; /* lazily acquired */ uint8_t *hash; /* dynamic hash from identify() */ } Code; Code code[DTHandle]; typedef struct { DTHandle me; DTHandle rep; uint8_t *hash; /* static hash from ...::cdHash() */ } StaticCode; StaticCode staticCode[DTHandle]; codesign$1:::static-create / arg1 == kernel[pid].rep / { staticCode[arg0].me = kernel[pid].staticCode = arg0; staticCode[arg0].rep = arg1; printf("%8u %s[%d]%s(%p=KERNEL[%p])\n", timestamp, execname, pid, probename, arg0, arg1); } codesign$1:::static-create / arg1 != kernel[pid].rep / { staticCode[arg0].me = arg0; staticCode[arg0].rep = arg1; printf("%8u %s[%d]%s(%p,%s[%p])\n", timestamp, execname, pid, probename, arg0, rep[arg1].path, arg1); } codesign$1:::dynamic-create / arg1 == 0 / { code[arg0].me = kernel[pid].code = arg0; printf("%8u %s[%d]%s(%p=KERNEL)\n", timestamp, execname, pid, probename, arg0); } codesign$1:::dynamic-create / arg1 == kernel[pid].code / { code[arg0].me = arg0; printf("%8u %s[%d]%s(%p,)\n", timestamp, execname, pid, probename, arg0); } codesign$1:::dynamic-create / arg1 != 0 && arg1 != kernel[pid].code / { code[arg0].me = arg0; code[arg0].host = arg1; printf("%8u %s[%d]%s(%p,%p)\n", timestamp, execname, pid, probename, arg0, arg1); } security_debug$1:::sec-destroy / code[arg0].me == arg0 / { code[arg0].me = 0; printf("%8u %s[%d]destroy code(%p)\n", timestamp, execname, pid, arg0); } security_debug$1:::sec-destroy / staticCode[arg0].me == arg0 / { staticCode[arg0].me = 0; printf("%8u %s[%d]destroy staticCode(%p)\n", timestamp, execname, pid, arg0); } /* * Identification operations */ codesign$1:::guest-identify-* { printf("%8u %s[%d]%s(%p,%d,%s[%p])\n", timestamp, execname, pid, probename, arg0, arg1, rep[staticCode[arg2].rep].path, arg2); code[arg0].staticCode = arg2; } codesign$1:::guest-cdhash-* { self->hash = code[arg0].hash = (uint8_t *)copyin(arg1, sizeof(Hash)); printf("%8u %s[%d]%s(%p,H\"%02x%02x%02x...%02x%02x\")\n", timestamp, execname, pid, probename, arg0, self->hash[0], self->hash[1], self->hash[2], self->hash[18], self->hash[19]); } codesign$1:::static-cdhash { self->hash = staticCode[arg0].hash = (uint8_t *)copyin(arg1, sizeof(Hash)); printf("%8u %s[%d]%s(%p,H\"%02x%02x%02x...%02x%02x\")\n", timestamp, execname, pid, probename, arg0, self->hash[0], self->hash[1], self->hash[2], self->hash[18], self->hash[19]); } /* * Guest registry/proxy management in securityd */ typedef struct { DTHandle guest; string path; uint32_t status; uint8_t *hash; } SDGuest; SDGuest guests[DTHandle, DTHandle]; /* host x guest */ securityd*:::host-register { printf("%8u HOST DYNAMIC(%p,%d)\n", timestamp, arg0, arg1); } securityd*:::host-proxy { printf("%8u HOST PROXY(%p,%d)\n", timestamp, arg0, arg1); } securityd*:::host-unregister { printf("%8u HOST DESTROYED(%p)\n", timestamp, arg0); } securityd*:::guest-create { guests[arg0, arg2].guest = arg2; guests[arg0, arg2].path = copyinstr(arg5); guests[arg0, arg2].status = arg3; printf("%8u GUEST CREATE(%p,%s[0x%x],host=0x%x,status=0x%x,flags=%d)\n", timestamp, arg0, guests[arg0, arg2].path, arg2, arg1, arg3, arg4); } securityd*:::guest-cdhash / arg2 != 0 / { self->hash = guests[arg0, arg1].hash = (uint8_t *)copyin(arg2, sizeof(Hash)); printf("%8u GUEST HASH(%p,%s[0x%x],H\"%02x%02x%02x...%02x%02x\")\n", timestamp, arg0, guests[arg0, arg1].path, arg1, self->hash[0], self->hash[1], self->hash[2], self->hash[18], self->hash[19]); } securityd*:::guest-cdhash / arg2 == 0 / { printf("%8u GUEST HASH(%p,%s[0x%x],NONE)\n", timestamp, arg0, guests[arg0, arg1].path, arg1); } securityd*:::guest-change { printf("%8u GUEST CHANGE(%p,%s[0x%x],status=0x%x)\n", timestamp, arg0, guests[arg0, arg1].path, arg1, arg2); } securityd*:::guest-destroy { printf("%8u GUEST DESTROY(%p,%s[0x%x])\n", timestamp, arg0, guests[arg0, arg1].path, arg1); } /* * Signing Mach-O allocation tracking */ codesign$1:::allocate-arch { printf("%8u %s[%d]%s(%s,%d)\n", timestamp, execname, pid, probename, copyinstr(arg0), arg1); } codesign$1:::allocate-archn { printf("%8u %s[%d]%s((0x%x,0x%x),%d)\n", timestamp, execname, pid, probename, arg0, arg1, arg2); } codesign$1:::allocate-write { printf("%8u %s[%d]%s(%s,offset 0x%x,%d of %d)\n", timestamp, execname, pid, probename, copyinstr(arg0), arg1, arg2, arg3); } codesign$1:::allocate-validate { printf("%8u %s[%d]%s(%s,%d)\n", timestamp, execname, pid, probename, copyinstr(arg0), arg1); } /* * Evaluation tracking */ codesign$1:::eval-dynamic-start { printf("%8u %s[%d]%s(%p,%s)\n", timestamp, execname, pid, probename, arg0, copyinstr(arg1)); } codesign$1:::eval-dynamic-end { printf("%8u %s[%d]%s(%p)\n", timestamp, execname, pid, probename, arg0); }