You are on page 1of 9

"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.

Google Search

"TPM DRM" In Mac OS X: A Myth That Won't


Die the book
© Amit Singh. All Rights Reserved. Written in December 2007 Preface
Table of Contents
Read Reviews
Beating a Dead Horse Source Code
Errata
Apple began shipping x86-based Macintosh computers in early 2006. Even before that,
Buy Now
people had noted the presence of a Trusted Platform Module (TPM) in the prerelease
Developer Transition Kit machines. There had been much talk and much furor about the the book — extras
system-wide "DRM" the TPM would enforce. However, the x86-based Macintoshes were Bonus Content
released with the TPM being not used—for anything. FAQ

In October 2006, I wrote about the TPM and its "use" in Mac OS X. Since Apple provided no software
software or firmware drivers for the TPM, I also wrote and released an open-source TPM Downloads
driver for Mac OS X. In particular, I said the following. Bug Reports

meta
"Apple's TPM Keys"
Blog
Forums
The media has been discussing "Apple's use of TPM" for a long time now. There have
Home
been numerous reports of system attackers bypassing "Apple's TPM protection" and
finding "Apple's TPM keys." Nevertheless, it is important to note that Apple does not about
use the TPM. If you have a TPM-equipped Macintosh computer, you can use the TPM About the Author
for its intended purpose, with no side effect on the normal working of Mac OS X.
Résumé
Contact
This turned out to be rather hard for many to believe. After all, Apple was somehow tying
elsewhere
Mac OS X to their own hardware; and the early x86 Macs did have onboard TPMs. Some
kernelthread.com
people concluded—incorrectly so—that the TPM must be involved. As is often the norm on
the Internet, such conclusions transmogrified into infallible myths. I found the nonsense
rather hard to believe—I had expected the open-source TPM driver to dispel such myths.
The driver and associated software allowed the user to do "whatever" with the TPM: enable adsense
it, use it for their own purposes, even disable it—without affecting the normal functioning of
Mac OS X. Besides, the TPM was a common piece of hardware in modern-day computers,
and indeed, it often sat unused in computers from several vendors. In the summer of 2006,
when the Mac Pro was introduced, x86-based Macs stopped having onboard TPMs
altogether. Even then , conspiracy theorists still came up with explanations, as they now
concluded that the TPM somehow must have been incorporated with the CPU.

Around the same time in 2006, I also wrote about kernel-level binary protection in Mac OS
X. As that article explains, it is actually binary protection that ties Mac OS X to a specific
class of hardware. The mechanism is partly implemented as a special-purpose virtual
memory (VM) pager that is interposed between the kernel's higher layer and the vnode
pager.

Nevertheless, to this day, the myth continues. The TPM, although uninvolved in any Mac-
related DRM (and non-existent in Macs for a long while now), often ends up being cannon
fodder in Mac-related religious warfare.

The Key to Understanding: S imple Minded C alculations


So what's missing from the popular understanding of this issue? The keys to understanding
how it works are all there in the aforementioned articles on TPM and binary protection. Well,
almost all there. There's the small matter of the "key". Since we are talking about binary
encryption, what about the key to decrypt? I've been often asked about the nature of this

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 1
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
encryption, what about the key to decrypt? I've been often asked about the nature of this
key—in particular, where does it come from?

Well, the key itself wasn't so interesting to me since I'm not allergic to Apple hardware.
However, not knowing this bit of information seems to have led some to the "logical"
conclusion that the key must be held within the... TPM! Perhaps the TPM that's "inside the
CPU"? Except that there's no such thing. Ironically, it appears that even those who initially
subverted Mac OS X to intercept the keys believed them to be "TPM keys".

My interest in this is no different from my academic interest in the working of Mac OS X.


The same interest had led to Mac OS X Internals, whose goal was to answer many, many
questions. Here, we have a case where the wrong answer has taken a life of its own. I can't
help but find such semi-wilful ignorance annoying.

What is the correct answer?

The key (actually, a pair of 32-byte values) comes from the System Management Controller
(SMC). Unlike in the case of a TPM, accessing this key involves no cryptography, no random
numbers, no hardware security—it's merely obfuscation. Just as you can use I/O Kit
interfaces to retrieve motion sensor data and numerous other readings from the SMC, you
can retrieve the key—no number crunching involved. You don't even need superuser
privileges. In fact, assuming you know how to access hardware from user-space, a program
to do this would be quite straightforward to write on Mac OS X—perhaps around 50 lines of
C.

Figure 1 shows such a program.

Why Obfuscate?
I view the obfuscation approach as engineering pragmaticism in solving difficult
problems. The problem here was that of making Mac OS X reasonably difficult to
deploy (from a legal standpoint—that is, would involve reverse engineering, breaking
the EULA, etc.) on non-Apple hardware. A solution based on the TPM would have been
fraught with numerous problems for the developers and maintainers of the solution.
As is not uncommon in such cases, obfuscation can be an effective enough solution.

/*
* smc_read.c: Written for Mac OS X 10.5. Compile as follows:
*
* gcc -Wall -o smc_read smc_read.c -framework IOKit
*/
# i n c l u d e <stdio.h>
# i n c l u d e <IOKit/IOKitLib.h>
typedef struct {
uint32_t key;
uint8_t __d0[22];
uint32_t datasize;
uint8_t __d1[10];
uint8_t cmd;
uint32_t __d2;
uint8_t data[32];
} AppleSMCBuffer_t;
int
mainn(void)
{
io_service_t service = I O S e r v i c e G e t M a t c h i n g S e r v i c e
e(kIOMasterPortDefault,
IOServiceMatching g("AppleSMC"));
i f (!service)
r e t u r n -1;
io_connect_t port = (io_connect_t)0;

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 2
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
io_connect_t port = (io_connect_t)0;
kern_return_t kr = I O S e r v i c e O p e n
n(service, m a c h _ t a s k _ s e l f
f(), 0, &port);
IOObjectRelease e(service);
i f (kr != kIOReturnSuccess)
r e t u r n kr;
AppleSMCBuffer_t inputStruct = { 'OSK0', {0}, 32, {0}, 5, }, outputStruct;
size_t outputStructCnt = s i z e o f
f(outputStruct);
kr = I O C o n n e c t C a l l S t r u c t M e t h o d
d((mach_port_t)port, (uint32_t)2,
(cc o n s t void*)&inputStruct, s i z e o f f(inputStruct),
(void*)&outputStruct, &outputStructCnt);
i f (kr != kIOReturnSuccess)
r e t u r n kr;
int i = 0;
f o r (i = 0; i < 32; i++)
printf
f("%c", outputStruct.data[i]);
inputStruct.key = 'OSK1';
kr = I O C o n n e c t C a l l S t r u c t M e t h o d
d((mach_port_t)port, (uint32_t)2,
(cc o n s t void*)&inputStruct, s i z e o f f(inputStruct),
(void*)&outputStruct, &outputStructCnt);
i f (kr == kIOReturnSuccess)
f o r (i = 0; i < 32; i++)
printf f("%c", outputStruct.data[i]);
printf
f("\n");
return IOServiceClose
e(port);
}

Figure 1. Reading information from the SMC

Anybody's Protected Binaries


With this knowledge, there's a particularly interesting experiment you can do. You know
that the operating system knows how to execute binaries protected (encrypted) with
predefined keys. You understand how this mechanism works. You also know where the keys
come from. Therefore, you should be able to protect your own binaries and have the system
execute them—that'd be kind of neat.

Figure 2 shows a program that will create an encrypted copy of the given Mach-O
executable. (This program is a bit longer than the one in Figure 1 because it tries to do
sanity checking on the input and handles some error cases.) Specifically, it will encrypt the
binary's text segment. Recall from our discussion of protected binaries that the first 3 pages
of a protected segment are not encrypted. Therefore, the binary that you wish to encrypt
must have a text segment larger than 3 pages in size. (That may not be the case with a
typical "Hello, World!" C program.)

/*
* apb_encrypt.c: Written for Mac OS X 10.5. Compile as follows:
*
* gcc -Wall -o apb_encrypt apb_encrypt.c -framework IOKit -lcrypto
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <copyfile.h>
# i n c l u d e <mach/mach.h>
# i n c l u d e <mach/machine.h>

# i n c l u d e <mach-o/fat.h>

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 3
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
# i n c l u d e <mach-o/fat.h>
# i n c l u d e <mach-o/loader.h>
# i n c l u d e <openssl/aes.h>
# i n c l u d e <IOKit/IOKitLib.h>
# d e f i n e A P B _ U N P R O T E C T E D _ H E A D E R _ S I Z E (3 * PAGE_SIZE)
#define APB_CRYPT_AES_KEY_SIZE (256)
#define APB_FAT_MAX_ARCH (5)
s t a t i c char header_page[PAGE_SIZE];
s t a t i c char data_page[PAGE_SIZE];
s t a t i c char xcrypted_page[PAGE_SIZE];
s t a t i c boolean_t apb_initialize
e(int, AES_KEY*, AES_KEY*);
s t a t i c int apb_encrypt_page
e(c
c o n s t void*, void*);
s t a t i c io_connect_t A p p l e S M C _ C o n n e c t
t(void);
s t a t i c void AppleSMC_Disconnect t(io_connect_t);
s t a t i c IOReturn AppleSMC_Read32 2(io_connect_t, uint32_t, uint8_t*);
typedef struct {
uint32_t key;
uint8_t __d0[22];
uint32_t datasize;
uint8_t __d1[10];
uint8_t cmd;
uint32_t __d2;
uint8_t data[32];
} AppleSMCBuffer_t;
s t a t i c io_connect_t
AppleSMC_Connect t(void)
{
io_connect_t port = (io_connect_t)0;
io_service_t service = I O S e r v i c e G e t M a t c h i n g S e r v i c e
e(kIOMasterPortDefault,
IOServiceMatching g("AppleSMC"));
i f (!service) {
r e t u r n port;
}
kern_return_t kr = I O S e r v i c e O p e n
n(service, m a c h _ t a s k _ s e l f
f(), 0, &port);
IOObjectRelease e(service);
i f (kr != kIOReturnSuccess) {
r e t u r n (io_connect_t)0;
}
r e t u r n port;
}
s t a t i c void
AppleSMC_Disconnect t(io_connect_t port)
{
(void)IIO S e r v i c e C l o s e
e(port);
}
s t a t i c IOReturn
AppleSMC_Read32 2(io_connect_t port, uint32_t key, uint8_t* data32)
{
AppleSMCBuffer_t inputStruct = { 0, { 0 }, 32, { 0 }, 5, };
AppleSMCBuffer_t outputStruct;
size_t outputStructCnt = s i z e o f
f(outputStruct);
inputStruct.key = key;
IOReturn kr = I O C o n n e c t C a l l S t r u c t M e t h o d
d((mach_port_t)port, (uint32_t)2,
(cc o n s t void*)&inputStruct, s i z e o f f(inputStruct),
(void*)&outputStruct, &outputStructCnt);
i f (kr != kIOReturnSuccess) {
r e t u r n kr;
}
(void)m
me m c p y
y(data32, outputStruct.data, 32);
r e t u r n kIOReturnSuccess;
}
s t a t i c boolean_t
apb_initialize e(__unused int mode, AES_KEY* key1, AES_KEY* key2)
{
boolean_t result = FALSE;

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 4
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
boolean_t result = FALSE;
io_connect_t port = A p p l e S M C _ C o n n e c t
t();
i f (!port) {
r e t u r n result;
}
IOReturn ret;
uint8_t data32[32] = { 0 };
ret = A p p l e S M C _ R e a d 3 2
2(port, 'OSK0', data32);
i f (ret != kIOReturnSuccess) {
g o t o out;
}
AES_set_encrypt_key y(data32, APB_CRYPT_AES_KEY_SIZE, key1);
ret = A p p l e S M C _ R e a d 3 2
2(port, 'OSK1', data32);
i f (ret != kIOReturnSuccess) {
g o t o out;
}
AES_set_encrypt_key y(data32, APB_CRYPT_AES_KEY_SIZE, key2);
result = TRUE;
out:
AppleSMC_Disconnect
t(port);
r e t u r n result;
}
s t a t i c int
apb_encrypt_page e(cc o n s t void* in, void* out)
{
s t a t i c AES_KEY key1, key2;
s t a t i c boolean_t initialized = FALSE;
i f (initialized == FALSE) {
initialized = a p b _ i n i t i a l i z e
e(AES_ENCRYPT, &key1, &key2);
i f (initialized == FALSE) {
r e t u r n -1;
}
}
c o n s t unsigned char* _in = (c
c o n s t unsigned char*)in;
unsigned char* _out = (unsigned char*)out;
unsigned char apb_null_iv1[AES_BLOCK_SIZE] = { 0x0, };
unsigned char apb_null_iv2[AES_BLOCK_SIZE] = { 0x0, };
AES_cbc_encrypt t(_in, _out, PAGE_SIZE / 2, &key1, apb_null_iv1, AES_ENCRYPT);
_in += (PAGE_SIZE / 2);
_out += (PAGE_SIZE / 2);
AES_cbc_encrypt t(_in, _out, PAGE_SIZE / 2, &key2, apb_null_iv2, AES_ENCRYPT);
r e t u r n 0;
}
int
mainn(int argc, char** argv)
{
int fd_in = -1;
int fd_out = -1;
i f (argc != 3) {
fprintff(stderr, "usage: %s <infile> <outfile>\n", argv[0]);
exit
t(1);
}
fd_in = o p e n
n(argv[1], O_RDONLY);
i f (fd_in < 0) {
perrorr("open");
exit
t(1);
}
off_t base = (off_t)0;
off_t ebase_begin = (off_t)0;
off_t ebase_end = (off_t)0;
uint32_t n = 0;
int ret = 0;
ssize_t nbytes = p r e a d
d(fd_in, header_page, PAGE_SIZE, (off_t)0);
i f (nbytes != PAGE_SIZE) {
ret = -1;
g o t o out;

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 5
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
g o t o out;
}
uint32_t magic = *(uint32_t*)header_page;
s t r u c t mach_header* mh = (s
s t r u c t mach_header*)0;
# i f d e f __LITTLE_ENDIAN__
i f (magic == FAT_CIGAM) {
s t r u c t fat_header* fh = (s s t r u c t fat_header*)header_page;
uint32_t nfat_arch = n t o h l l(fh->nfat_arch);
i f (nfat_arch > APB_FAT_MAX_ARCH) {
fprintf f(stderr, "too many architectures in Universal binary\n");
ret = -1;
g o t o out;
}
s t r u c t fat_arch* fa = (s s t r u c t fat_arch*)((char*)header_page +
sizeoff(s
s t r u c t fat_header));
f o r (n = 0; n < nfat_arch; n++, fa++) {
i f (n nt o h l
l(fa->cputype) == CPU_TYPE_X86) {
base = (off_t)n nt o h l
l(fa->offset);
nbytes = p r e a d d(fd_in, header_page, PAGE_SIZE, base);
i f (nbytes != PAGE_SIZE) {
fprintf f(stderr, "failed to read Universal binary\n");
ret = -1;
g o t o out;
}
mh = (s s t r u c t mach_header*)header_page;
break k;
}
}
} e l s e i f (magic == MH_MAGIC) {
mh = (s s t r u c t mach_header*)header_page;
i f (mh->cputype != CPU_TYPE_X86) {
fprintf f(stderr, "this program supports only x86 architecture\n");
ret = -1;
g o t o out;
}
} else {
fprintf f(stderr, "not an appropriate Mach-O file\n");
ret = -1;
g o t o out;
}
#else
# e r r o r This file can only be compiled on Intel.
#endif
s t r u c t segment_command* text = (s
s t r u c t segment_command*)0;
uint32_t ncmds = mh->ncmds;
s t r u c t load_command* lc =
(ss t r u c t load_command*)((char*)mh + s i z e o f
f(s
s t r u c t mach_header));
f o r (n = 0; n < ncmds; n++) {
i f (lc->cmd == LC_SEGMENT) {
s t r u c t segment_command* sc = (s s t r u c t segment_command*)lc;
i f (s st r c m p
p(sc->segname, SEG_TEXT) == 0) {
text = sc;
break k;
}
}
lc = (s s t r u c t load_command*)((char*)lc + lc->cmdsize);
}
i f (!text) {
fprintf f(stderr, "failed to find text segment\n");
ret = -1;
g o t o out;
}
i f (text->flags & SG_PROTECTED_VERSION_1) {
fprintf f(stderr, "already encrypted\n");
ret = -1;
g o t o out;
}
i f (text->filesize < APB_UNPROTECTED_HEADER_SIZE) {
fprintf f(stderr, "text segment is too small to protect\n");
ret = -1;
g o t o out;
}
off_t archbase_begin = (off_t)(text->fileoff + APB_UNPROTECTED_HEADER_SIZE);
off_t archbase_end =
archbase_begin + (off_t)(text->filesize - APB_UNPROTECTED_HEADER_SIZE);

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 6
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
archbase_begin + (off_t)(text->filesize - APB_UNPROTECTED_HEADER_SIZE);
ebase_begin = base + archbase_begin;
ebase_end = base + archbase_end;
fd_out = o p e nn(argv[2], O_RDWR | O_CREAT | O_EXCL, 0755);
i f (fd_out < 0) {
perror r("open");
ret = -1;
g o t o out;
}
ret = f c o p y f i l e
e(fd_in, fd_out, (copyfile_state_t)0, COPYFILE_ALL);
i f (ret) {
perror r("copyfile");
ret = -1;
g o t o out;
}
text->flags |= SG_PROTECTED_VERSION_1;
nbytes = p w r i t e
e(fd_out, header_page, PAGE_SIZE, base);
i f (nbytes != PAGE_SIZE) {
perror r("pwrite");
ret = -1;
g o t o out;
}
off_t count = ebase_end - ebase_begin;
i f (count % PAGE_SIZE) {
fprintf f(stderr, "text segment not a multiple of page size\n");
ret = -1;
g o t o out;
}
w h i l e (count > 0) {
nbytes = p r e a d d(fd_in, data_page, PAGE_SIZE, ebase_begin);
i f (nbytes != PAGE_SIZE) {
perror r("pread");
ret = -1;
g o t o out;
}
ret = a p b _ e n c r y p t _ p a g e
e(data_page, xcrypted_page);
i f (ret) {
fprintf f(stderr, "failed to encrypt page\n");
g o t o out;
}
nbytes = p w r i t e e(fd_out, xcrypted_page, PAGE_SIZE, ebase_begin);
i f (nbytes != PAGE_SIZE) {
perror r("pwrite");
ret = -1;
g o t o out;
}
ebase_begin += (off_t)PAGE_SIZE;
count -= (off_t)PAGE_SIZE;
}
ret = 0;

out:
i f (fd_in >= 0) {
close e(fd_in);
}
i f (fd_out >= 0) {
close e(fd_out);
i f (ret) {
unlinkk(argv[2]);
}
}
exit
t(ret);
}

Figure 2. Creating a protected binary

Let us look at our encryption program in action. Recall from the binary protection
discussion that the LC_SEGMENT load command for an encrypted segment has a special bit
(0x8) set. In that discussion, we used the otool command-line program to view the load
commands in a Mach-O file. Figure 3 shows an excerpt from the output of running otool
on the binaries for ls and the Finder. We see that the flags field has the 0x8 bit set in the
Finder's case, which is a protected binary, but not in the case of ls.

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 7
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.

$ otool -l /bin/ls
/bin/ls:
Load command 0
...
Load command 1
cmd LC_SEGMENT
cmdsize 260
segname __TEXT
vmaddr 0x00001000
vmsize 0x00005000
fileoff 0
filesize 20480
maxprot 0x00000007
initprot 0x00000005
nsects 3
flags 0x0
Section
...

$ cd /System/Library/CoreServices/Finder.app/Contents/MacOS
$ otool -l Finder
Finder:
Load command 0
...
Load command 1
cmd LC_SEGMENT
cmdsize 668
segname __TEXT
vmaddr 0x00001000
vmsize 0x00404000
fileoff 0
filesize 4210688
maxprot 0x00000007
initprot 0x00000005
nsects 9
flags 0x8
Section
...

Figure 3. Using otool to view Mach-O load commands

Let us now create a protected copy of ls. Figure 4 shows a sequence of commands to do
that assuming you've downloaded apb_encrypt.c to /tmp/.

$ cd /tmp
$ gcc -Wall -o apb_encrypt apb_encrypt.c -framework IOKit -lcrypto
$ cp /bin/ls ./ls
$ ./apb_encrypt ls ls.protected
$ ls -l ls*
total 288
144 -r-xr-xr-x 1 you wheel 73696 Jan 31 22:52 ls
144 -r-xr-xr-x 1 you wheel 73696 Jan 31 22:54 ls.protected
$ diff ls ls.protected
Binary files ls and ls.protected differ
$ otool -l ls.protected
...
Load command 1
cmd LC_SEGMENT
cmdsize 260
segname __TEXT
vmaddr 0x00001000
vmsize 0x00005000
fileoff 0
filesize 20480
maxprot 0x00000007
initprot 0x00000005
nsects 3
flags 0x8
Section
...
$ strings ls
...
usage: ls [-ABCFGHLPRSTWabcdefghiklmnopqrstuwx1] [file ...]
humanize_number
buf != NULL
humanize_number.c
suffix != NULL
...
$ strings ls.protected
...
q9D@
J[19
:Jk#
.Wnu
`=SI
...
$ otool -t -v -V ls.protected | grep 'bad opcode'
...
0000510c .byte 0xb2 #bad opcode
000051b9 .byte 0xf1 #bad opcode
000051c8 .byte 0x8e #bad opcode
000051f1 .byte 0x77 #bad opcode
...
$ ./ls.protected -l

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 8
"TPM DRM" In Mac OS X: A Myth That Won't Die 01/16/10 02:37 p.
$ ./ls.protected -l
total 288
-r-xr-xr-x 1 you wheel 73696 Jan 31 22:52 ls
-r-xr-xr-x 1 you wheel 73696 Jan 31 22:54 ls.protected

Our protected version of ls is automagically decrypted at runtime by the operating system.


If you're so inclined, you could even encrypt your entire system (!) this way with your own
keys . Of course, you'd have to re-encrypt the system's apple-protected binaries with your
own keys too, and write a kernel extension that sets your keys up as the ones to be used by
the operating system. You'll then have a system unlike any other. (Don't do it—I wasn't
being serious.)

All contents of this site, unless otherwise noted, are ©1994-2010 Amit Singh. All Rights Reserved. Terms of Use

http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ Page 9

You might also like