Hacker for Hire

Are you tired of inaccuracies in technology in film and tv?

  • Hire me, bitches.

Examples:

    • Law and Order CI – You pronounced Peter Junger’s name incorrectly
    • Mr. Robot – Postmates credit (you can’t get there from here) Incorrect avenue – there is a correct one. I know. I have 1000s in credit
    • Limitless – Red alert tech talk (really?) and DDOS virus… not in that way
    • CSI (CRINGE!!!!!!!)

 

  • Jurassic Park: I’ve used Unix… the filesystem hierarchy didn’t look like this! Maybe because I was using solaris…

 

  • NCIS – Misogynistic assumption the hacker is a HE…

Reasons to hire me:

  • I’m tired of your inaccurate info too.
  • You can reduce, even ELIMINATE your Misogynistic language!
  • It’s easier to hire me as a consultant than to wait for me to write novels.
  • I spoke at a hacker conference. LOL.
  • I am a hacker.
  • I know some pretty famous hackers… Seriously. I wickr with capn crunch. ūüėõ
  • Right now, I’m working remotely, have little contact with real humans other than hackers anyway – I am part of the hacker mindset… LOL
  • I’m qualified, in fact in a few minutes, I plan to overclock my Raspberry pi…
  • I’m fairly affordable

ūüôā

contact me @  aicra at ch1x0r.net

Preface – Chixor

390502_2830086036487_1751010168_n - Copy

 

Does anyone really read these prefaces? Fine. Whatever. Here’s some front matter and some back_ground.

I didn’t necessarily choose hacking as hacking chose me. It was a given. I was faster than most computers, at least faster than any computer I had access to. Seriously. My father worked comms in the Marine Corp for a couple tours in Vietnam. ¬†I never knew what it was called or that there was even a name for what he did, but he was really good at things like tapping phone lines, listening in on neighbors for fun, wiring, electronic engineering and such. When I was child, we had no access to the internet. We had, the library and tribal knowledge. In 4th grade, I found a project in the local library that required a large battery and some basic electronic components. I relished math games. I was still a girl. I loved flowers and kittens. I also loved¬†making mud pies and snow forts.

I went to a religious college. It was so backward, the rules required females to wear dresses or skirts. No pants.

Let’s just say, I was a rebel!

I took programming. There were a few ladies in the course.

In that computer programming course, I was constantly reminded that women couldn’t program. They couldn’t code and they couldn’t possibly understand electronics or computing. I was the sole female left in the course.

My father’s words constantly ringing in my mind, “You can do anything a man can do.” Sure, I could. Didn’t matter. I spent years working in electronics at Dad’s company. The professor accused me of cheating, you know – since I was a lady and I got an A on the exam. How could that be right!

I said, “retest me”.

He said, “You would have time to learn it by then.”
“Your argument was I couldn’t learn it though!” I retorted. “Ask me anything!” I didn’t hide the look of hatred on my face.

He said, “Maybe you already¬†studied it after the test so you’d be prepared.”

I said, “Please. But go ahead then. If I get it wrong, you’ll be justified. Right. Let’s end this.”

Was I really dueling my prof?

Yes. Yes I was.

“What is ROM?”

“Read only memory.”

“Yeah, you know the acronym.” Was he… laughing at me?

“Ok. Read only memory is non volatile memory that does not tend to remain when a device is restarted.”

“Congratulations, you can regurgitate a definition.” He turned away.

Oh, no. He did not turn away from me. I continued to explain what the difference between ROM and RAM was down to details.

“How is it possible you could understand this!” He exclaimed.

“My father was president of Apature Industries – an electronics and speaker wire company. I worked there since I was 7. I know how to solder, I can strip wire, connect transformers, create intercom systems and I have some basic programming.”

“Oh, well that explains it!” he said. He turned to the other men in the course. “A man taught her. Well, since I wouldn’t be the one teaching you and you already know some of this, you may just be the first girl to complete my course.”

Yes I did. Yes, I most certainly did!

program HelloWorld(output);
begin
  Write('Hello, world!')
  {no ";" is required after the last statement of a block -
   adding one adds a "null statement" to the program;}
end.

PASCAL…. bleh. My first program was a spreadsheet application.

1983 – the year of the hacker in movies

Everything I learned about hacking Рstarted in 1983. This is when War Games and Superman III came out.

Superman III

In this movie, Richard Pryor’s character incorporates programming into money using the 1/2 cent rule, he uses social engineering to gain access to a facility he needs to change weather. He’s not really a genius in the way Lex is. He’s a clown.

War Games

In this movie, I learned electronics is king! I learned social engineering, brute force, and not to trust anyone or anything!

I’m going to write a book now. I have no choice. I am so freaking tired of inaccurate hacking information in movies and books. I mean come on… really. You want to write about hacking but you lack the intelligence, the jargon, the knowledge, the experience and understanding… welll US.

Step away from the keys – You have 0 on us. We actually respect and admire – accurate, up to date information. You don’t know us. You can read the jargon file all day long. That doesn’t make you a part of our collective, our community. You can call us white or black… you can’t even imagine all the gray we face. The lines and decisions we make on a daily basis that structure our future. You have no clue as to our moral and ethical codes. Some have none. Some live them, some walk a fine line.

You report about us and make stories up about us.

Seriously, you’re lucky to have a computer. If it were up to me, I’d start a movement to remotely disable computers of those who report inaccuracies.

Sometimes, you get to me. I think you’re accurate. You seem accurate. And then… you make a simple mistake. Such as… calling an attack a virus. Or using a term we would never use – like intellectual property. The virtual or digital product is neither property and most of the time, it cannot be considered intellectual in any way, shape, or form.

This is my voice. Listen or don’t. I’m tired of your L4M3 Writing… your L4M3 shows, your L4M3 efforts into entertaining me – a hacker, a chixor. I… have… had … enough.

L…A…M…E ¬†You are just a wannabe.

Seriously. If you want to write tech – don’t omit the tech. If you want to write about hacking – don’t do it inaccurately. There are times I say, That could NEVER work. It’s close. You are close. SO close. But… No… You take me almost there. To that place I desire to dwell in my dreams. To the place where fantasy and reality are blurred. The plots fantastic. The stories and characters amazing. Then… you make a simple error.

And it’s over.

I don’t know that my characters will engage. I don’t know that my plot will hook.

But damn… at least my hacking info… will be solid. Bitches.

 

SCALE – LA March 2-6

15x_logo_lg

 

Be there… or be.. ok, whatever. I’ll be there, man.

Getting out of the cave! Woohoo!

 

Find me – Talk tech, DMCA, whatever. Something for me to think about until I leave the cave again!

irc: aicra

or maybe if you see me… Catch up with me.

 

I know… most of my peeps don’t use social media.

https://www.linkedin.com/in/marciawilbur

…and I’m irrelevant to speak… but I like to hang out with the legal minds.

Beale Screamer – FREED US!

eff-coding-540x270

 

The entire concept behind anonymity empowering our community comes from one manifesto. One personage – Beale.

Thanks Beale. I, personally, shall remember you always – well, until dementia kicks in… ūüôā

http://www.mp3newswire.net/stories/2001/DRMhack.html

http://www.mp3newswire.net/stories/2001/mad.html  РESSAY

 

Note from Ch1x0r: In many cases… reporters stated “his” in reference to Beale Screamer. This is simply indicative of the misogynistic era of computing during those days. Male reporters assumed that Beale was 1 person, a male. In fact, this may have been true. Only a few choice people know the truth. Those few – took a vow to protect their source at all costs.

The ethical question was raised after dinner. I was on my way to the 2600 hearings.  

“What are your thoughts on protecting a source?” ¬†

That’s a no brainer for me.

After a short discussion, there came a knock at the door… the rest, is history.

th

Article content found below.

Citing the abuse of monopoly power and the Digital Millennium Copyright Act () as his reason for developing the hack – we will just assume that the challenge and the ability to listen to music unsecured are also reasons – Beale Screamer posted this note to the Justice Department:
Justice Department: Maybe this should really be addressed to the state officials, since it looks like the current U.S. administration doesn’t care too much about monopoly powers being abused. But for whoever is interested, there is a very serious anti-competitive measure in this software [DRM]. In particular, for various modules of the software to be used, you must supply a certified public key for communication. Guess who controls the certification of public keys? Microsoft. So if someone wants to make a competing product, which integrates well with the Windows OS, you will need to get Microsoft’s permission and obtain a certificate from them. I don’t know what their policy is on this, so don’t know if this power will be abused or not. However, it has the potential for being a weapon Microsoft can use to knock out any competition to their products.

***Note from Ch1x0r***
(someone is making assumptions here…)

Beale Screamer goes into even more detail on¬†his¬†(<—— ASSUMPTION!) essay “Mad as Hell about the DMCA” which he includes with the FreeMe download on a file called “Philosophy”. There under the heading How Things Should Be Beale Screamer writes:

COPY:
23 October 2001
Source: Google archive of sci.crypt.

These are the messages Beale Screamer posted anonymously to newsgroup sci.crypt. Arranged chronologically here, but dates and times were probably assigned by the anonymous remailers.

(Any messages missed, please tell: jya@pipeline.com(Microsoft DRM – Technical description, 2001-10-18 09:10:41 PST

FreeMe source – Multiswap.c,h, 2001-10-18 15:34:26 PST

FreeMe source – ecc.c,h, 2001-10-18 15:40:26 PST

FreeMe source – msdrm.c,h, 2001-10-18 16:20:33 PST

FreeMe README file, 2001-10-18 16:26:19 PST

FreeMe source – main.c, 2001-10-18 16:31:25 PST
Viewing message <17620O8I37182.4630787037@anonymous.poster>
From: Anonymous (anonymous@anonymous.poster.com)
Subject: Microsoft DRM – Technical description
Newsgroups: sci.crypt
Date: 2001-10-18 09:10:41 PST
—–BEGIN PGP SIGNED MESSAGE—–

Microsoft’s Digital Rights Management Scheme – Technical Details

By “Beale Screamer”

This document describes version 2 of the Microsoft Digital Rights

Management (MS-DRM), as applied to audio (.wma files). The sources

for this material are varied, and some of the information might be

slightly incomplete; however, the fundamental ideas are solid and

easily verified. There is no attempt at describing the older version

1 of DRM. While version 1 is widely used (probably more widely than

version 2!), and the scheme is somewhat simpler, the purpose of this

is to describe the latest technology and not necessarily allow all

existing systems to be broken. The ideas described here are also

implemented in the software originally distributed with this document

(but as an independent piece, so the software may or may not be

available from where you have obtained this document), so a real

implementation can be examined. Not all of the information here is

needed in order to write the software that removes the encryption, but

some of the more interesting points surrounding the MS-DRM scheme and

software are given even if not necessary. Also note that no code is

included in this document, either real code or pseudo-code. All

that’s in this document is a straight mathematical discussion, which

should be fully protected under the 1st Amendment to the

U.S. Constitution. I have no doubt that the corporate entities that

this document offends will attempt to suppress it, but I don’t think

any argument they make could hold up to Constitutional scrutiny.

The basic components of MS-DRM involve use of elliptic curve

cryptography (ECC) for public key cryptography, DES for a block

cipher, RC4 for a stream cipher, and SHA-1 for a hash function. There

is also a block cipher which I haven’t seen before, used in the MS-DRM

system to build a MAC, or keyed hash function. This cipher will be

explained completely below, and while the remaining algorithms are

well-known, more will be said about Microsoft’s use of ECC below.

In the discussion and examples below, all numbers are expressed in

hexadecimal in the standard ordering (most-to-least significant)

unless otherwise stated. The actual bytes comprising large numbers in

any code are stored little endian, so at times it is convenient to

look at data in that ordering, and this will be clearly marked when it

is done.

One confusing item is that binary data sent back and forth is encoded

using Base64, but not using the standard algorithm! For some reason,

Microsoft has decided to use the non-alphanumeric character ‘*’

instead of ‘/’, and ‘!’ instead of ‘+’ in some places, and in other

places they replace ‘/’ with ‘@’ and ‘!’ with ‘%’. This means that

any software dealing with these strings cannot use a standard Base64

decoder, but must use a custom-build decoder.

FILES INVOLVED

Several key DLLs are kept in \windows\system that relate to the MS-DRM

scheme.

drmv2clt.dll: Provides basic DRM version 2 functionality

blackbox.dll: Provides basic, machine-specific crypto for MS-DRM.

Functionality replaced by IndivBox.key when the local

system has been “individualized.”

The other interesting place for files is in \windows\All Users\DRM

(this location is not necessarily fixed but comes from the registry

entry HKEY_LOCAL_MACHINE\Software\Microsoft\DRM\DataPath). Here’s a

sampling of some of the files in this directory (these are hidden

system files, so be sure to turn on “view all files” in order to see

them!):

IndivBox.key: Despite the extension, this is really a DLL

that is an “individualized” version of blackbox.dll

drmv2.lic: The file of licenses (a structured IStorage file)

drmv2.sst: “Secure state” for each of the licenses. Also an IStorage

file, but each stream is RC4 encrypted.

v2ks.bla: The version 2 “key store” – this is where all the public/private

keys are kept (encrypted, of course!).

v2ksndv.bla: The individualized version 2 “key store.”

A SIMPLE BLOCK CIPHER (MULTISWAP)

Microsoft is using a very simple block cipher to create a message

authentication code (MAC). As this is not a standard algorithm, I

will describe it fully. The main operations in this cipher are

32-bit multiplications and swaps of the two halves of 32-bit words, so

I have called this cipher the “MultiSwap” cipher.

The MultiSwap cipher works on 64-bit blocks, using a key that consists

of 12 32-bit words, and a current state (or initialization vector)

that is 64-bits long. In the Microsoft implementation, the least

significant bits of all 12 words are set to 1, although once the

cipher is understood it is clear that really only 10 of the words

require this bit to be set. The basic operation of the cipher is a

transformation that is done on the first 32-bit word of the plaintext

block using the first 6 key words, and then repeated for the other

plaintext word and remaining key words.

Let k[0], k[1], k[2], k[3], k[4], and k[5] denote the first 6 key

words, and let s[0] and s[1] denote the two words of the state. To

transform a 32-bit input word x, first define the following function

f(a)=swap(swap(swap(swap(swap(a*k[0])*k[1])*k[2])*k[3])*k[4]) + k[5]

where * is multiplication modulo 2^(32), and “swap” is an operation

which exchanges the two 16-bit halves of a 32-bit word. The complete

transformation of a 32-bit word x then consists of s[1]=s[1]+f(x+s[0])

and s[0]=f(x+s[0]). This is first done with the value x set to the

first 32 bits of the input, and then repeated with x set to the second

32 bits and the using keys k[6] through k[11]. The output of the

block cipher is the new state s[0] and s[1].

The reason this block cipher can be inverted is because all the key

words are odd, which means they have multiplicative inverses modulo

2^(32). To invert f(), just do the operations in the reverse order:

first subtract off k[5], then do the multiply/swap operations with the

inverses of k[4] through k[0]. Notice that only the multiplicative

key words really need to be odd, so there is no reason for the least

significant bit of k[5] or k[11] to be set; however, Microsoft sets

these bits anyway.

This block cipher is never used for encryption, but is used to create

a message authentication code (MAC) in the standard way. Assuming the

length of the message to be hashed is a multiple of 8 bytes (64 bits),

the cipher is initialized with a state of all zeros, and then used to

encrypt the entire data. The output of the last block (the final

state) is the MAC for that message. This is used in computing packet

keys to encrypt protected content by MS-DRM, as will be explained

later.

ELLIPTIC CURVE CRYPTOGRAPHY

For ECC, Microsoft is using an elliptic curve over Zp, where p is a

160 bit prime number (given below). The curve consists of the points

that lie on the curve y^2=x^3+ax+b, where the operations are done over

the field Zp and a and b are coefficients that are given below.

All values are represented as packed binary values: in other words, a

single value over Zp is encoded simply as 20 bytes, stored in little

endian order. A point on the elliptic curve is therefore a 40 byte

block, which consists of two 20 byte little endian values (the x

coordinate followed by the y coordinate). Here are the parameters for

the elliptic curve used in MS-DRM:

p (modulus): 89abcdef012345672718281831415926141424f7

coefficient a: 37a5abccd277bce87632ff3d4780c009ebe41497

coefficient b: 0dd8dabf725e2f3228e85f1ad78fdedf9328239e

generator x: 8723947fd6a3a1e53510c07dba38daf0109fa120

generator y: 445744911075522d8c3c5856d4ed7acda379936f

Order of curve: 89abcdef012345672716b26eec14904428c2a675

These constants are fixed, and used by all parties in the MS-DRM

system. The “nerd appeal” of the modulus is high when you see this

number in hexadecimal: it includes counting in the hexadecimal, as

well as the digits of fundamental constants e, pi, and sqrt(2).

In order to use this public key system, any user must have a

private/public key pair. Since the security of the system relies

pretty heavily on the private keys remaining secret (even from the

user of the system on which they reside), they are carefully hidden.

In fact, there are keys hidden in various files that are used,

including blackbox.dll, v2ks.bla, and IndivBox.key. For example, once

the player has been individualized, IndivBox.key is created, and there

are at least two keys embedded into this file: a 64-bit key used for

RC4, and a 160-bit private key for use in ECC. The ECC private key is

used as the basic client key (the corresponding public key is stored

unencrypted in the key store file, and used as the initial part of the

“client id” sent when requesting a license), and additional key pairs

are stored in part of the keystore file (v2ks.bla or v2ksndv.bla),

encrypted with the RC4 key.

These secret keys are stored in linked lists that contain 32 bits per

node (so the key as a whole is not in contiguous memory), interspersed

with the code in the library (IndivBox.key for example). The idea is

that they can be read by that library, used internally by that

library, and never communicated outside the library. Since the

IndivBox.key file is shuffled in a random way for each client, these

keys would be extremely difficult to extract from the file itself.

Fortunately, we don’t have to: these keys are part of the object state

that is maintained by this library, and since the offset within this

object of these secret keys is known, we can let the library itself

extract the secret keys! The code for this simply loads up the “black

box” library, has it initialize an instance of the object, and then

reads the keys right out of that object. This is clearly a weakness

in the code which can be corrected by the DRM software fairly easily,

but for now it is the basis of our exploit.

GETTING A LICENSE

Each protected media file is encrypted with a “content key” that will

unlock the packets of the media stream. We describe briefly how a

license (containing a content key) is obtained for information

purposes, but the license acquisition protocol is not really important

for unlocking that content. Simply use the MS Media player, have it

request and decrypt the licenses, store them in drmv2.lic, and then we

can extract them directly from that file.

A protected media file is apparently recognized by the presence of a

DRMV2 object in the .wma file header. This object has GUID

298ae614-2622-4c17-b935-dae07ee9289c, and contains an XML object 6

bytes into the data part of the object. Among other things, this

header contains a “KID” element identifying the key used to unlock the

content. The drmv2.lic file is then checked to see if a license with

this KID exists locally. If the license doesn’t exist, a license

request is formed, which sends an encrypted “client id” to the license

server. This is sent as a “challenge,” which consists of 168 bytes in

the MS-Base64 encoding. The first 80 bytes are two ECC points, which

make up an ECC encrypted random session key, and the remaining 88

bytes are the “client id” encrypted using RC4 and the session key.

The ECC encryption is done using a public key that seems to be fixed

for all clients, so it is safe to assume that this corresponds to a

private key that is common to all license servers and built in to that

side of the system (without access to the server side code, it was

impossible to find the corresponding private key).

After some interaction, the license comes back as mime type

application/x-drm-v2, as an escaped XML-encoded license in the

following format

<LICENSERESPONSE><LICENSE version=”x.x.x.x”>

…base64 encoded license… </LICENSE></LICENSERESPONSE>

where x.x.x.x is most likely “2.0.0.0”. To make things tricky for a

sniffer, the license is actually RC4 encrypted using the same session

key that was established by the client when sending the challenge.

The client then decrypts the license and stores it in the drmv2.lic

file.

GETTING THE CONTENT KEY

Getting the content key from a license is pretty easy once the client

knows what its public/private key pairs are, and has a copy of the

license obtained from drmv2.lic. The license entry is an XML object

with an element for “ENABLINGBITS”, which has sub-elements ALGORITHM

(which should have type “MSDRM”), PUBKEY, VALUE, and SIGNATURE. The

PUBKEY element should match one of the client’s public keys, or else

there a problem! The VALUE element is the ECC-encrypted content key,

which can be decrypted by the private key that corresponds to the

given PUBKEY.

The content key has a specific format: the y coordinate is ignored,

and when the x coordinate is written in storage order (little endian),

the first byte is the length of the content key (which may always be

7), which is followed by that many bytes of the content key. While

the content key is tied to the encoded media file (which may be common

to many users), the enabling bits value will be different for each

user, and tied to that user’s public/private keys. Because of this,

licenses are not transferable from one user to another, even though

the media files themselves are (the new user must obtain his own

license from the license server).

We go through an example now of finding a content key. In this

example, we have identified our public and private keys as the

following values:

Public key x: 1957f96f3327a25bba52166ad7fcc74087b9734b

Public key y: 8939e1b1ed988182d34d17ebbcb0e03a82d062e7

Private key: 757ff01b853496452eea0b0646c3a357a6f33509

We’re looking at a file RIAALuvsMe.wma, and find in the header the

following bit of XML:

<KID>nA67jM7dNGIUQIkP5v7hSQ==</KID>

The actual KID seems to be a Base64 encoding of a GUID, but it is

treated as a string (uninterpreted) by the software, so the origin

doesn’t seem to make much difference.

The license is inside the drmv2.lic file, which is a structured “DOC

file”, meaning it can be accessed through the IStorage and IStream

interfaces (and it can be browsed by the Microsoft Visual Studio “DOC

File Viewer” tool if you’re curious). The top level drmv2.lic file

has a lower-level IStorage object for each KID, which can contain a

set of licenses for each KID. In order to guarantee valid IStorage

names, the KID is first processed to change all ‘/’ characters to ‘@’,

and all ‘!’ to ‘%’. The names of the IStream objects containing the

licenses again look like Base64 encoded GUIDs, which turn out to be

the LID (license ID?) element stored in the license. This can be

verified once the license is obtained, but we’re not sure how to

generate LID’s from the content header information, and so can’t

directly open the appropriate LID stream. Instead, we simply

enumerate through all available streams for this KID, testing each one

for a PUBKEY element (see below) that we know. This is taken to be

the license for this content. While this is really just a guess as to

the proper workings, it seems to work fine in all our tests.

Inside the license we find the following XML (this has been formatted

so that it’s easier to look at – in the actual file this would all be

on one line).

<ENABLINGBITS>

<ALGORITHM type=”MSDRM”></ALGORITHM>

<PUBKEY type=”machine”>

S3O5h0DH*NdqFlK6W6InM2*5VxnnYtCCOuCwvOsXTdOCgZjtseE5iQ==

</PUBKEY>

<VALUE>

VEsbPedfwrybrpkg0fhoOfe5eB9ef0R7QTxgX7NbtMIFK!h*4Pk7ek

PUqlDIRqYwQkgCGE0r0qtQdCUYszT!b7XedCIpsApQjstaFmafahM=

</VALUE>

<SIGNATURE>

KpxCm6lSXH8dTPI359jToftSEuLiP9v*zpHAy!kDEhlYkw6mkfQzlg==

</SIGNATURE>

</ENABLINGBITS>

The SIGNATURE element above is just random garbage. We didn’t make a

real signature for this example (among other things, we don’t have a

certified public key, which would have to follow this in a real

license. Requiring such a signature keeps people from creating their

own licenses, since only those that have been issued valid

certificates can do so).

First look at the PUBKEY part. If this is run through a Base64

decoder (modified for the MS character set as described earlier) you

get the following binary values, shown below as a memory dump:

0000: 4B 73 B9 87 40 C7 FC D7 6A 16 52 BA 5B A2 27 33

0010: 6F F9 57 19 E7 62 D0 82 3A E0 B0 BC EB 17 4D D3

0020: 82 81 98 ED B1 E1 39 89

Notice how this is exactly our public key from above, stored in little

endian order! So this license is for our machine.

Next, take the VALUE element above, run it through a Base64 decoder,

and interpret the 80 byte result as 4 20-byte values stored in little

endian order. These four numbers are as follows:

Encrypted u.x: & 1f78b9f73968f8d12099ae9bbcc25fe73d1b4b54

u.y: & 7a3bf9e07fe82b05c2b45bb35f603c417b447f5e

v.x: & 18257450abd22b4d1802484230a646c850aad443

v.y: & 136a9f66165acb8e500ab0292274deb56ffe34b3

To decrypt this value, first we multiply the point u by our private

key, resulting in the point

x: 399c72d525a9b65b7543a3e3adc88ce0f6a38db5

y: 66cfa6bdbfbb93b906b22deb36792363d8e8adc2

and then subtract this point from v to get

x: c91590616b4b3707

y: 753e24e50d437e147b4998376f163dc27b639a7a

Since x is so short, we have almost certainly gotten our content key.

Writing in storage order (little endian), x is

0000: 07 37 4B 6B 61 90 15 C9

which means that the content key has length 7 (from the first byte),

and the actual key is the string of bytes 374B6B619015C9.

DECRYPTING THE CONTENT

The content encryption process is simpler to explain than decryption,

so we start with that. The content key is not used directly, but is

processed for several different uses. First, the content key is

hashed using the SHA-1 hashing algorithm, producing a 20-bit output.

The first 12 bytes of this output are used as an RC4 key, and a block

of 16 words (or 64 bytes) of zeros is encrypted. The least

significant bits of the first 12 words of this output are all set, and

are used as the MultiSwap key. The next 2 words are the encryption

in-whitening mask and the next 2 words are the encryption

out-whitening mask (this will be explained later). The last 8 bytes

of the original SHA-1 hash output are used as a DES key.

To encrypt the content so that packets can be accessed randomly (for

seeks), the content cannot be encrypted as one single stream.

However, to strengthen the cipher we also don’t want to re-use the

same key for every packet. To satisfy both of these goals, MS-DRM

uses the following scheme to encrypt a packet: First, the packet (with

size rounded down to a multiple of 8 bytes) is run through

MultiSwapMAC to produce a 64-bit MAC. For some reason, the 32-bit

halves of this MAC are swapped before further processing. Next, the

entire packet is RC4 encrypted using the swapped MAC as an 8-byte RC4

key. The 8-byte MAC (with swapped halves) is then run through a

“whitened DES” by first XORing with the in-whitening mask, then

running through DES (using the DES key from the last paragraph), and

then XORing the result with the out-whitening mask. The resulting 8

bytes are then placed in the final encrypted packet, overwriting the

last full 8-byte block (not the last 8 bytes of the packet, but

blocking the packet from the beginning into 8 byte pieces, and

overwriting the last full piece).

To decrypt such a packet, first locate the last full 8-byte block, run

it through the whitened DES decryption, and the result is used as an

RC4 key to decrypt the packet. This will produce the correct packet

except for 8 bytes: those in the position of the last full 8-byte

block are wrong, since they were overwritten in the last phase of the

encryption. However, by swapping the halves of the RC4 key, we have

the MAC for the original packet up to and including the original bytes

in this position. Since the MAC is actually created out of a block

cipher, we can recover the original 8 bytes as follows: run the entire

packet through MultiSwapMAC up to the block in question, but not

including it. This output is the next-to-last state seen by

MultiSwapMAC in the encryption MAC computation, and we just recovered

the final output of the MAC, so we can put these two values into the

block cipher decryption to obtain the original data of this 8-byte

block. The original 8-bytes are placed back into the packet, and now

the entire original contents are restored!

This is a pretty clever scheme: by using a MAC constructed from a

block cipher, individual packet keys can be computed and encoded into

the encrypted packet with absolutely no increase in space, and since

the size is maintained nothing in the structure of the content file

(describing packet sizes or other parameters) needs to be changed at

all. The encrypted content can be completely transparent to

applications that deal with .wma files in a non-content-sensitive

manner.

We finish this section showing an example of content decryption, using

the content key from the previous paragraph. We first process the

content key by running it through SHA-1 to obtain

15 CB 92 F9 97 2E C8 75 29 4F 12 65 36 B6 C6 DB AC A2 40 35

The first 12 bytes are used as an RC4 key to encrypt a block of all

zeros, giving

0000: 80 0A 2D 48 D1 FD 7E ED 83 69 4A 7D A5 D5 EE C4

0010: 4E E1 64 52 D1 71 98 26 9A F3 14 E3 51 C8 B6 92

0020: D4 93 E4 57 97 6D 63 EF 0E 06 07 54 F7 DD ED 38

0030: E8 CA A0 D0 83 13 F1 DB C1 70 AE 56 61 7D FB 94

The first 48 bytes are interpreted as 12 32-bit words, stored little

endian, and are saved as the MultiSwap key after setting all least

significant bits. So the key values are k[0]=0x482D0A81,

k[1]=0xED7EFDD1, etc. The last 16 bytes of the RC4-encrypted block

are the whitening masks for DES, and the key for DES is given as the

last 8 bytes of the SHA-1 hash value.

Assume we get a packet with size 1450 bytes, which is 181 8-byte

blocks followed by 2 additional bytes. Numbering the byte positions

as 0 through 1449, we look at the bytes in positions 1440 through 1447

(the last full 8-byte block), and find that they are

A8 49 65 36 A2 33 18 09

XORing with the encryption out-whitening mask (the last 8 bytes from

the RC4-encrypted block above) we get

69 39 CB 60 C3 4E E3 9D

and decrypting with DES using key 36 B6 C6 DB AC A2 40 35 gives

FD EF 98 7D 8B 77 72 FD

and finally XORing with the encryption in-whitening mask (the

next-to-last 8-byte piece in the RC4-encrypted block above) gives

15 25 38 AD 08 64 83 26

This is then used as an RC4 key to decrypt the packet. To replace

bytes at positions 1440 through 1447 with the correct values, take

the RC4 value and swap the words around to get

08 64 83 26 15 25 38 AD

This is the MultiSwapMAC of the input packet using bytes 0 through

1447. We run bytes 0 through 1439 through MultiSwapMAC to get

D9 F7 D9 53 A9 6E 14 D9

and then use this as the state input, along with the original MAC

output as the data input to the MultiSwapDecode function to obtain

DA 05 D8 EB 97 FE 1E 7B

These 8 bytes are placed in positions 1440 through 1447, and then the

entire original packet is restored.

OTHER ISSUES

Communication between different DLL modules is encrypted and checked

at multiple points. This works roughly as follows: Objects are

initialized with communication parameters by sending certified public

keys to the object you want to communicate with. The second object

verifies these certificates, generates a random session key (which it

uses to generate a MultiSwap key in addition to the use as a session

key), and sends the encrypted session key back to the calling object.

Future “sensitive communication” is RC4 encrypted with the session key,

and run through MultiSwapMAC to verify integrity (after padding with

zeros to make the data a multiple of 64 bits). This is done for data

sent both to and from the object.

Presumably, this is done so that anyone monitoring parameters passed

between DLL modules wouldn’t see any “sensitive data,” although its

use for this purpose is pretty limited. However, it does lead to some

interesting and strange situations: when blackbox is sent a packet to

decrypt, it decrypts it, and then immediately *re-encrypts* it using

the session key to send it back to the media player! So in decrypting

a packet, the computer actually goes through a decrypt/encrypt/decrypt

sequence of operations!

One very important effect of this scheme is that Microsoft fully

controls who gets to write modules that interact with the basic

Microsoft media modules. Without a certified public key (and the

corresponding private key) it is impossible to write a compatible DLL

that interfaces with their code. Since Microsoft controls the issuing

of certified public keys, they also have complete control over who is

allowed to make compatible and competing products. Microsoft’s

reputation for being generous to competitors is well-known, so this

effectively gives Microsoft a technically guaranteed monopoly power.

Of course, these certificates and private keys must be distributed

with any “Microsoft blessed” software as well, and in fact exist in

the media player and blackbox DLLs. They’re not hard to extract, if

you know where to look, but I won’t give them here. They would be of

limited use anyway, since Microsoft also has a “revocation list”

mechanism built in to the Media player software, meaning that they can

revoke any of these certificates at their whim, remotely disabling any

software that depends on that certificate for communication.

—–BEGIN PGP SIGNATURE—–

Version: 2.6.2

iQCVAwUBO5qt3JCr1f2GXCalAQE8ygP9Gb4Dm0ZQ5GePjAIfMFyqYVtUNSUUfj7A

3ZLwbMwUtnRHeYDGWRJEqvJMPf4SujKHcwQL3LtefrhH7dOn6r4AyUQV6ymezpd/

AMY53ONufawU+T8YgilEe2WCDRc4Y/uDbQFZIhcPQ+H78nzFSvdj+FzQ7pKrxsIr

QWe1ZNP4xfY=

=WL0q

—–END PGP SIGNATURE—–
______________________________________________________________________________
Posted Via Binaries.net = SPEED+RETENTION+COMPLETION = http://www.binaries.net

From: Frog2 (FrogRemailer@NoReply.Invalid.com)
Subject: FreeMe source – Multiswap.c,h
Newsgroups: sci.crypt
Date: 2001-10-18 15:34:26 PST
Split into .c and .h files

——————— Start of MultiSwap.h ———————-

#if !defined( _MULTISWAP_H_ )
#define _MULTISWAP_H_

typedef struct multiswapkey_st {
struct {
int multikey[5];
int multiinv[5];
int additive;
} round[2];
} MULTISWAPKEY;

void MultiSwapSetKey(MULTISWAPKEY * out, unsigned int *datain);
void MultiSwapEncode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout);
void MultiSwapMAC(MULTISWAPKEY * key, unsigned int *data, int num64bits,
unsigned int *out);
void MultiSwapDecode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout);

#endif

——————— Start of MultiSwap.c ———————-

#include “MultiSwap.h”
static int mod232inv(int x)
{
int rval = x;
int i;

for (i = 0; i < 30; i++)
rval *= rval * x;

return rval;
}
void MultiSwapSetKey(MULTISWAPKEY * out, unsigned int *datain)
{
int i;

for (i = 0; i < 5; i++) {
out->round[0].multikey[i] = datain[i] | 1;
out->round[0].multiinv[i] = mod232inv(datain[i] | 1);
out->round[1].multikey[i] = datain[6 + i] | 1;
out->round[1].multiinv[i] = mod232inv(datain[6 + i] | 1);
}

/* Note that the “| 1” part isn’t necessary here, since we don’t
* need multiplicative inverses. But they do it anyway!
*/

out->round[0].additive = datain[5] | 1;
out->round[1].additive = datain[11] | 1;
}
void MultiSwapEncode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout)
{
unsigned int cst = state[0];

cst += datain[0];
cst *= key->round[0].multikey[0];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multikey[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multikey[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multikey[3];
cst = (cst >> 16) | (cst << 16);
cst = cst * key->round[0].multikey[4] + key->round[0].additive;
dataout[1] = cst + state[1];

cst += datain[1];
cst *= key->round[1].multikey[0];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multikey[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multikey[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multikey[3];
cst = (cst >> 16) | (cst << 16);
cst = cst * key->round[1].multikey[4] + key->round[1].additive;
dataout[1] += cst;

dataout[0] = cst;
}
void MultiSwapMAC(MULTISWAPKEY * key, unsigned int *data, int num64bits,
unsigned int *out)
{
unsigned int state[2];
unsigned int newstate[2] = { 0, 0 };
int i;

for (i = 0; i < num64bits; i++) {
state[0] = newstate[0];
state[1] = newstate[1];
MultiSwapEncode(key, state, data + 2 * i, newstate);
}

out[0] = newstate[0];
out[1] = newstate[1];
}
void MultiSwapDecode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout)
{
unsigned int cst = datain[0];
unsigned int tmp;

dataout[1] = datain[1] – cst;
cst = (cst – key->round[1].additive) * key->round[1].multiinv[4];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[3];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[0];
tmp = dataout[1] – state[1];
dataout[1] = cst – tmp;
cst = tmp;

cst = (cst – key->round[0].additive) * key->round[0].multiinv[4];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[3];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[0];

dataout[0] = cst – state[0];
}

From: Nomen Nescio (nobody@dizum.com)
Subject: FreeMe source – ecc.c,h
Newsgroups: sci.crypt
Date: 2001-10-18 15:40:26 PST
Split into .c and .h files

——————— Start of ecc.h ———————-

#if !defined( _ECC_H )
#define _ECC_H

#include <openssl/bn.h>

/*
* If the “neg” field of the BIGNUM struct is set, then this point is
* the identity. This is a terrible way to do this, since it’s not
* clear what the future of this flag is — however, for now it works,
* and it’s fast…
*/

typedef struct eccpt_st {
BIGNUM *x, *y;
} ECCpt;

typedef struct eccparam_st {
BIGNUM *modulus; /* Curve is over Z_modulus */
BIGNUM *a, *b; /* Curve coefficients */
ECCpt generator; /* Generator for our operations */
ECCpt pubkey; /* Public key */
BIGNUM *privkey; /* Corresponding private key */
} ECC;
ECC *ECC_new_set(BIGNUM * p, BIGNUM * a, BIGNUM * b, ECCpt g);
void ECC_free(ECC * ecc);
void ECCpt_init(ECCpt * pt);
void ECCpt_free(ECCpt * pt);
int ECCpt_is_valid_pt(ECCpt * a, ECC * ecc);
int ECCpt_is_equal(ECCpt * a, ECCpt * b);
void ECCpt_add(ECCpt * r, ECCpt * a, ECCpt * b, ECC * ecc);
void ECCpt_mul(ECCpt * r, ECCpt * a, BIGNUM * n, ECC * ecc);

#endif

——————— Start of ecc.c ———————-

/* General purpose elliptic curve routines. */

#include <stdio.h>
#include <stdlib.h>
#include “ecc.h”
static int ECC_param_ok(BIGNUM * p, BIGNUM * a, BIGNUM * b)
{
BIGNUM *tmp1, *tmp2;
BN_CTX *ctx;
int retval;

if ((p == NULL) || (a == NULL) || (b == NULL))
return 0;

tmp1 = BN_new();
tmp2 = BN_new();
ctx = BN_CTX_new();

BN_mod_mul(tmp1, a, a, p, ctx);
BN_mod_mul(tmp1, tmp1, a, p, ctx);
BN_lshift(tmp1, tmp1, 2);
BN_mod(tmp1, tmp1, p, ctx);

tmp2 = BN_new();
BN_set_word(tmp2, 27);
BN_mod_mul(tmp2, tmp2, b, p, ctx);
BN_mod_mul(tmp2, tmp2, b, p, ctx);

BN_add(tmp2, tmp1, tmp2);
BN_mod(tmp2, tmp2, p, ctx);

retval = !BN_is_zero(tmp2);

BN_CTX_free(ctx);
BN_free(tmp1);
BN_free(tmp2);

return retval;
}
ECC *ECC_new_set(BIGNUM * p, BIGNUM * a, BIGNUM * b, ECCpt g)
{
ECC *ecc;

if (!ECC_param_ok(p, a, b))
return NULL;

ecc = malloc(sizeof(ECC));
if (ecc != NULL) {
ecc->modulus = BN_dup(p);
ecc->a = BN_dup(a);
ecc->b = BN_dup(b);
ecc->generator.x = BN_dup(g.x);
ecc->generator.y = BN_dup(g.y);
ecc->pubkey.x = ecc->pubkey.y = NULL;
ecc->privkey = NULL;
}

return ecc;
}
void ECC_free(ECC * ecc)
{
if (ecc != NULL) {
BN_free(ecc->modulus);
ecc->modulus = NULL;
BN_free(ecc->a);
ecc->a = NULL;
BN_free(ecc->b);
ecc->b = NULL;
BN_free(ecc->generator.x);
ecc->generator.x = NULL;
BN_free(ecc->generator.y);
ecc->generator.y = NULL;
if (ecc->pubkey.x != NULL) {
BN_free(ecc->pubkey.x);
ecc->pubkey.x = NULL;
BN_free(ecc->pubkey.y);
ecc->pubkey.y = NULL;
}
if (ecc->privkey != NULL) {
BN_free(ecc->privkey);
ecc->privkey = NULL;
} free(ecc);
}
}
void ECCpt_init(ECCpt * pt)
{
pt->x = BN_new();
pt->y = BN_new();
}
void ECCpt_free(ECCpt * pt)
{
BN_free(pt->x);
pt->x = NULL;
BN_free(pt->y);
pt->y = NULL;
}
int ECCpt_is_valid_pt(ECCpt * a, ECC * ecc)
{
/* check that y^2 = x^3 + a x + b */
BIGNUM *tmp1, *tmp2;
BN_CTX *ctx;
int retval;

ctx = BN_CTX_new();
tmp1 = BN_dup(a->x);
BN_mod_mul(tmp1, tmp1, tmp1, ecc->modulus, ctx);
BN_add(tmp1, tmp1, ecc->a);
BN_mod_mul(tmp1, tmp1, a->x, ecc->modulus, ctx);
BN_add(tmp1, tmp1, ecc->b);
if (BN_cmp(tmp1, ecc->modulus) >= 0)
BN_sub(tmp1, tmp1, ecc->modulus);

tmp2 = BN_dup(a->y);
BN_mod_mul(tmp2, tmp2, tmp2, ecc->modulus, ctx);

retval = (BN_cmp(tmp1, tmp2) == 0);
BN_free(tmp1);
BN_free(tmp2);
BN_CTX_free(ctx);
return retval;
}
int ECCpt_is_equal(ECCpt * a, ECCpt * b)
{
if (a->x->neg && b->x->neg)
return 1;
return ((BN_cmp(a->x, b->x) == 0) && (BN_cmp(a->y, b->y) == 0));
}
void ECCpt_add(ECCpt * r, ECCpt * a, ECCpt * b, ECC * ecc)
{
BN_CTX *ctx;
BIGNUM *tmp1, *tmp2;
BIGNUM *lambda;

if (a->x->neg) {
BN_copy(r->x, b->x);
BN_copy(r->y, b->y);
return;
}

if (b->x->neg) {
BN_copy(r->x, a->x);
BN_copy(r->y, a->y);
return;
}

tmp1 = BN_new();
if (BN_cmp(a->x, b->x) == 0) {
BN_add(tmp1, a->y, b->y);
if (BN_cmp(tmp1, ecc->modulus) == 0) {
BN_free(tmp1);
r->x->neg = 1; /* Set to identity */
return;
}
}

ctx = BN_CTX_new();
tmp2 = BN_new();
lambda = BN_new();
if (ECCpt_is_equal(a, b)) {
BN_set_word(tmp1, 3);
BN_mod_mul(tmp1, tmp1, a->x, ecc->modulus, ctx);
BN_mod_mul(tmp1, tmp1, a->x, ecc->modulus, ctx);
BN_add(tmp1, tmp1, ecc->a);
BN_mod(tmp1, tmp1, ecc->modulus, ctx);
BN_lshift1(tmp2, a->y);
BN_mod_inverse(tmp2, tmp2, ecc->modulus, ctx);
BN_mod_mul(lambda, tmp1, tmp2, ecc->modulus, ctx);
} else {
BN_sub(tmp1, b->x, a->x);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
tmp2 = BN_mod_inverse(NULL, tmp1, ecc->modulus, ctx);
BN_sub(tmp1, b->y, a->y);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
BN_mod_mul(lambda, tmp1, tmp2, ecc->modulus, ctx);
}

BN_mod_mul(tmp1, lambda, lambda, ecc->modulus, ctx);
BN_sub(tmp1, tmp1, a->x);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
BN_sub(tmp2, tmp1, b->x);
if (tmp2->neg)
BN_add(tmp2, ecc->modulus, tmp2);

BN_sub(tmp1, a->x, tmp2);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
BN_mod_mul(tmp1, lambda, tmp1, ecc->modulus, ctx);
BN_sub(r->y, tmp1, a->y);
if (r->y->neg)
BN_add(r->y, ecc->modulus, r->y);

BN_free(r->x);
r->x = tmp2;
tmp2 = NULL;

BN_free(lambda);
BN_free(tmp1);
BN_CTX_free(ctx);
}
void ECCpt_mul(ECCpt * r, ECCpt * a, BIGNUM * n, ECC * ecc)
{
ECCpt tmp;
int numbits, i;

tmp.x = BN_dup(a->x);
tmp.y = BN_dup(a->y);
r->x->neg = 1;
numbits = BN_num_bits(n);
for (i = numbits – 1; i >= 0; i–) {
if (BN_is_bit_set(n, i))
ECCpt_add(r, r, &tmp, ecc);
if (i > 0)
ECCpt_add(r, r, r, ecc);
}
}

Viewing message <c9e08269164a5978d72b12be3bc77a6c@remailer.segfault.net>
From: Anonymous Coredump (mixmaster@remailer.segfault.net)
Subject: FreeMe source – msdrm.c,h
Newsgroups: sci.crypt
Date: 2001-10-18 16:20:33 PST
Split into .c and .h files

——————— Start of msdrm.h ———————-

#if !defined( _MSDRM_H_ )
#define _MSDRM_H_

#include <openssl/des.h>
#include “ecc.h”
#include “MultiSwap.h”

typedef unsigned char uchar;

#define MS_BN_LEN 20

typedef struct ms_bn_st {
uchar d[MS_BN_LEN];
} MS_BN;

typedef struct ms_eccpt_st {
MS_BN x, y;
} MS_ECCpt;

typedef struct contkey_st {
uchar ckey[MS_BN_LEN];
int ckeylen;
uchar keyhash[20];
des_key_schedule keysched;
uchar outmask[8];
uchar inmask[8];
MULTISWAPKEY hashkey;
} CONTKEY;

wchar_t *get_element(wchar_t * tag, wchar_t * str);
void MSDRM_decr_packet(uchar * data, int len, CONTKEY * ckey);
CONTKEY *MSDRM_init(wchar_t * license);

extern struct globalinfo_st {
int verbose;
void *fileheader;
char *ofname;
int packetlen;
int numpackets;
CONTKEY *content_key;
wchar_t *kid;
int hasV1header;
int hasV2header;
} globalinfo;

#endif

——————— Start of msdrm.c ———————-

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#include <windows.h>
#include <ole2.h>
#include <openssl/sha.h>
#include <openssl/rc4.h>
#include “ecc.h”
#include “msdrm.h”

extern void error_exit(char *msg);
extern void printwcs(wchar_t * msg);

typedef struct bboxobj_st {
void *jtable;
void *jtbl2;
void *jtbl3;
MS_BN ecprivkey;
MS_ECCpt ecpt1;
uchar clientid[84]; /* First part is public key */
uchar hwid[20];
uchar rc4key[6];
uchar pad1[2];
int numkeypairs;
uchar *keypairs;
} BBOXOBJ;

#define MAXKEYPAIRS 50

struct keypair_st {
MS_ECCpt public;
MS_BN private;
} keypair[MAXKEYPAIRS];
int numkeypairs = 0;
static MS_BN msec_mod = {
{0xf7, 0x24, 0x14, 0x14, 0x26, 0x59, 0x41, 0x31, 0x18, 0x28,
0x18, 0x27, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x89}
};

static MS_BN msec_a = {
{0x97, 0x14, 0xe4, 0xeb, 0x09, 0xc0, 0x80, 0x47, 0x3d, 0xff,
0x32, 0x76, 0xe8, 0xbc, 0x77, 0xd2, 0xcc, 0xab, 0xa5, 0x37}
};

static MS_BN msec_b = {
{0x9e, 0x23, 0x28, 0x93, 0xdf, 0xde, 0x8f, 0xd7, 0x1a, 0x5f,
0xe8, 0x28, 0x32, 0x2f, 0x5e, 0x72, 0xbf, 0xda, 0xd8, 0x0d}
};

static MS_BN msec_gx = {
{0x20, 0xa1, 0x9f, 0x10, 0xf0, 0xda, 0x38, 0xba, 0x7d, 0xc0,
0x10, 0x35, 0xe5, 0xa1, 0xa3, 0xd6, 0x7f, 0x94, 0x23, 0x87}
};

static MS_BN msec_gy = {
{0x6f, 0x93, 0x79, 0xa3, 0xcd, 0x7a, 0xed, 0xd4, 0x56, 0x58,
0x3c, 0x8c, 0x2d, 0x52, 0x75, 0x10, 0x91, 0x44, 0x57, 0x44}
};
static void printMSBN(MS_BN * num)
{
int i;
for (i = MS_BN_LEN – 1; i >= 0; i–)
fprintf(stderr, “%02x”, num->d[i]);
}
static BIGNUM *MS_BN_to_BN(MS_BN * msnum, BIGNUM * r)
{
uchar bigendian[MS_BN_LEN];
int i;

for (i = 0; i < MS_BN_LEN; i++)
bigendian[i] = msnum->d[MS_BN_LEN – 1 – i];

return BN_bin2bn(bigendian, MS_BN_LEN, r);
}
static void MS_ECCpt_to_ECCpt(MS_ECCpt * mspt, ECCpt * r)
{
MS_BN_to_BN(&mspt->x, r->x);
MS_BN_to_BN(&mspt->y, r->y);
}
static ECC *MSECC_new_set()
{
BIGNUM *tmod, *ta, *tb;
ECC *ecc;
ECCpt tg;

tmod = BN_new();
ta = BN_new();
tb = BN_new();
ECCpt_init(&tg);

MS_BN_to_BN(&msec_mod, tmod);
MS_BN_to_BN(&msec_a, ta);
MS_BN_to_BN(&msec_b, tb);
MS_BN_to_BN(&msec_gx, tg.x);
MS_BN_to_BN(&msec_gy, tg.y);

ecc = ECC_new_set(tmod, ta, tb, tg);

BN_free(tmod);
BN_free(ta);
BN_free(tb);
ECCpt_free(&tg);

return ecc;
}
static void MSECC_set_privkey(MS_BN * pk, ECC * ecc)
{
if (ecc->privkey == NULL)
ecc->privkey = BN_new();
MS_BN_to_BN(pk, ecc->privkey);
}
static void BN_to_MS_BN(BIGNUM * in, MS_BN * out)
{
MS_BN tmp;
int bytelen, i;

bytelen = BN_num_bytes(in);
if (bytelen > MS_BN_LEN)
error_exit
(“Bug in code: Result is too big in BN_to_MS_BN”);

for (i = 0; i < MS_BN_LEN; i++)
tmp.d[i] = 0;

BN_bn2bin(in, (uchar *) & tmp.d[MS_BN_LEN – bytelen]);

for (i = 0; i < MS_BN_LEN; i++)
out->d[i] = tmp.d[MS_BN_LEN – 1 – i];
}
static void MSECC_decrypt(MS_ECCpt * r, MS_ECCpt * ctext, ECC * ecc)
{
ECCpt u, v;

if (ecc->privkey == NULL)
error_exit
(“Bug in code: MSECC_decrypt called with no private key!”);

ECCpt_init(&u);
ECCpt_init(&v);
MS_ECCpt_to_ECCpt(&ctext[0], &u);
MS_ECCpt_to_ECCpt(&ctext[1], &v);

ECCpt_mul(&u, &u, ecc->privkey, ecc);
BN_sub(u.y, ecc->modulus, u.y);
ECCpt_add(&v, &v, &u, ecc);

BN_to_MS_BN(v.x, &r->x);
BN_to_MS_BN(v.y, &r->y);

ECCpt_free(&u);
ECCpt_free(&v);
}
static int MS_Base64Decode(wchar_t * str, char **buff)
{
wchar_t *cp;
char *ocp;
int len, val, count, block, ocount;

len = wcslen(str);
if ((*buff = malloc((len * 3) / 4)) == NULL)
error_exit(“Memory allocation failed in MS_Base64Decode.”);

ocp = *buff;
count = 0;
block = 0;
ocount = 0;
for (cp = str; *cp != L’\0′; cp++) {
if ((*cp >= L’A’) && (*cp <= L’Z’))
val = *cp – L’A’;
else if ((*cp >= L’a’) && (*cp <= L’z’))
val = *cp – L’a’ + 26;
else if ((*cp >= L’0′) && (*cp <= L’9′))
val = *cp – L’0′ + 52;
else if ((*cp == L’+’) || (*cp == L’!’))
val = 62;
else if ((*cp == L’/’) || (*cp == L’*’))
val = 63;
else if (*cp == L’=’) {
if (count == 2) {
*ocp++ = block >> 4;
ocount++;
} else {
*ocp++ = (block >> 10);
*ocp++ = (block >> 2) & 0xff;
ocount += 2;
} break;
} else
val = -1;

if (val >= 0) {
block = (block << 6) | val;
if (++count == 4) {
*ocp++ = block >> 16;
*ocp++ = (block >> 8) & 0xff;
*ocp++ = block & 0xff;
ocount += 3;
count = 0;
}
}
}

return ocount;
}
void MSDRM_decr_packet(uchar * data, int len, CONTKEY * ckey)
{
RC4_KEY rc4state;
int num64bits = len / 8;
uchar work2[8];
int i;
unsigned int pustate[2];
unsigned int tmpd[2];
uchar *keystart = data + (num64bits – 1) * 8;

if (len < 16) {
for (i = 0; i < len; i++)
data[i] ^= ckey->keyhash[i];
return;
}

for (i = 0; i < 8; i++)
keystart[i] ^= ckey->inmask[i];

des_ecb_encrypt((const_des_cblock *) keystart,
(des_cblock *) work2, ckey->keysched, 0);

for (i = 0; i < 8; i++)
work2[i] ^= ckey->outmask[i];

RC4_set_key(&rc4state, 8, work2);
RC4(&rc4state, len, data, data);

MultiSwapMAC(&ckey->hashkey, (unsigned int *) data, num64bits – 1,
pustate);
tmpd[0] = ((int *) work2)[1];
tmpd[1] = ((int *) work2)[0];
MultiSwapDecode(&ckey->hashkey, pustate, tmpd,
(unsigned int *) keystart);
}
static void MSDRM_setup(MS_BN * privkey, wchar_t * value, CONTKEY * out)
{
ECC *msecc;
MS_ECCpt dec;
RC4_KEY rc4state;
uchar rc4buff[64];
int len;
char *dynbuff;

msecc = MSECC_new_set();
MSECC_set_privkey(privkey, msecc);

len = MS_Base64Decode(value, &dynbuff);
MSECC_decrypt(&dec, (MS_ECCpt *) dynbuff, msecc);
free(dynbuff);

ECC_free(msecc);
msecc = NULL;

if ((uchar) dec.x.d[0] > MS_BN_LEN – 1)
error_exit(“Decrypted content key is too big!”);

out->ckeylen = (uchar) dec.x.d[0];
memcpy(out->ckey, &dec.x.d[1], out->ckeylen);

if (globalinfo.verbose) {
int i;
fprintf(stderr, “Content key:”);
for (i = 0; i < out->ckeylen; i++)
fprintf(stderr, ” %02x”, out->ckey[i]);
fprintf(stderr, “\n”);
}

SHA1(out->ckey, out->ckeylen, out->keyhash);

des_set_key_unchecked((des_cblock *) (&out->keyhash[12]),
out->keysched);

RC4_set_key(&rc4state, 12, out->keyhash);
memset(rc4buff, 0, sizeof(rc4buff));
RC4(&rc4state, sizeof(rc4buff), rc4buff, rc4buff);

memcpy(out->outmask, &rc4buff[48], 8);
memcpy(out->inmask, &rc4buff[56], 8);

MultiSwapSetKey(&out->hashkey, (unsigned int *) rc4buff);
}
/* Stupid little fake XML parser. */

static wchar_t *find_close(wchar_t * str)
{
while ((*str != L’\0′) && (*str != L’>’)) {
if (*str == L'”‘) {
if ((str = wcschr(str + 1, L'”‘)) == NULL)
return NULL;
} str++;
}

if (*str == L’\0′)
return NULL;
else
return str + 1;
}
wchar_t *get_element(wchar_t * tag, wchar_t * str)
{
int len = wcslen(tag);
wchar_t *tmptag;
wchar_t *start, *end;
wchar_t *rval = NULL;

if ((tmptag = malloc((len + 4) * sizeof(wchar_t))) == NULL)
error_exit(“Memory allocation failed in get_element (1)”);

swprintf(tmptag, L”<%s”, tag);

while (1) {
if ((start = wcsstr(str, tmptag)) == NULL)
goto exit;
if (!iswalnum(start[len + 1]))
break;
str = start + len + 1;
}

swprintf(tmptag, L”</%s>”, tag);
end = wcsstr(str, tmptag);

if (end == NULL) {
goto exit;
} else {
wchar_t *realstart = find_close(start);
if ((realstart == NULL) || (realstart > end)) {
goto exit;
} else {
wchar_t *tmp =
malloc((end – realstart +
1) * sizeof(wchar_t));
if (tmp == NULL)
error_exit
(“Memory allocation failed in get_element (2)”);
memcpy(tmp, realstart,
(end – realstart) * sizeof(wchar_t));
tmp[end – realstart] = L’\0′;
rval = tmp;
}
}

exit:
free(tmptag);
return rval;
}

/*
* getDRMDataPath allocates extra room on the end (20 wchars) for
* appending a filename
*/
static wchar_t *getDRMDataPath()
{
HKEY key_drm;
long stat;
DWORD dtype, dlen;
wchar_t *buff;

stat =
RegOpenKeyEx(HKEY_LOCAL_MACHINE, “Software\\Microsoft\\DRM”, 0,
KEY_READ, &key_drm);
if (FAILED(stat))
return NULL;

stat =
RegQueryValueEx(key_drm, “DataPath”, NULL, NULL, NULL, &dlen);
if (FAILED(stat))
return NULL;

if ((buff =
(wchar_t *) malloc(dlen + 20 * sizeof(wchar_t))) == NULL)
error_exit(“Memory allocation failed in getDRMDataPath”);

stat =
RegQueryValueEx(key_drm, “DataPath”, NULL, &dtype,
(uchar *) buff, &dlen);
if (FAILED(stat)) {
free(buff);
return NULL;
}

RegCloseKey(key_drm);

return buff;
}

int fileExistsA(char *fname)
{
return (GetFileAttributes(fname) != -1);
}

int fileExistsW(wchar_t * fname)
{
char buffer[MAX_PATH];
int len;

len = wcslen(fname);
if (WideCharToMultiByte
(CP_ACP, 0, fname, len + 1, buffer, MAX_PATH, NULL, NULL) == 0)
return 0;

return fileExistsA(buffer);
}

void getKSFilename(wchar_t * ksname, char *libname)
{
wchar_t *basepath = getDRMDataPath();
wchar_t currks[MAX_PATH], lastks[MAX_PATH];
char abasepath[MAX_PATH];
char currlib[MAX_PATH], lastlib[MAX_PATH];
int fnum;

if (basepath != NULL) {
WideCharToMultiByte(CP_ACP, 0, basepath,
wcslen(basepath) + 1, abasepath,
MAX_PATH, NULL, NULL);
swprintf(lastks, L”%s\\v2ks.bla”, basepath);
swprintf(currks, L”%s\\v2ksndv.bla”, basepath);
sprintf(lastlib, “BlackBox.dll”);
sprintf(currlib, “%s\\IndivBox.key”, abasepath);
fnum = 1;
while (fileExistsW(currks) && (fileExistsA(currlib))) {
fnum++;
wcscpy(lastks, currks);
swprintf(currks, L”%s\\v2ks%03x.bla”, basepath,
fnum);
strcpy(lastlib, currlib);
sprintf(currlib, “%s\\Indiv%03x.key”, basepath,
fnum);
}
wcscpy(ksname, lastks);
strcpy(libname, lastlib);
free(basepath);
}
}
static int getkeypairs()
{
HMODULE mylib;
int rval;
BBOXOBJ *bbobj;
char errmsg[100];
wchar_t KSFilename[MAX_PATH];
char BBoxLib[MAX_PATH];
int i;

getKSFilename(KSFilename, BBoxLib);
if (globalinfo.verbose) {
fprintf(stderr, “BlackBox library to use: %s\n”, BBoxLib);
fprintf(stderr, “Keystore to use: “);
printwcs(KSFilename);
fprintf(stderr, “\n”);
}
mylib = LoadLibraryA(BBoxLib);
if (mylib == NULL) {
DWORD err = GetLastError();
sprintf(errmsg, “Failed loading library. Err code %08x”,
err);
error_exit(errmsg);
} else {
typedef int (WINAPI * createfn) (BBOXOBJ **,
unsigned short *);
createfn create =
(createfn) GetProcAddress(mylib,
“IBlackBox_CreateInstance2”);
if (create == NULL)
error_exit(“Failed finding proc address.”);
else {
rval = (*create) (&bbobj, KSFilename);

if (bbobj == NULL) {
sprintf(errmsg,
“Failed to create a black box object (err code %08x)\n”,
rval);
error_exit(errmsg);
}

if (globalinfo.verbose) {
fprintf(stderr,
“Created BlackBox instance – extracting key pairs\n”);
}

memcpy(&keypair[0].private, &bbobj->ecprivkey, 20);
memcpy(&keypair[0].public, bbobj->clientid, 40);
numkeypairs = bbobj->numkeypairs + 1;
for (i = 0; i < bbobj->numkeypairs; i++) {
memcpy(&keypair[i + 1].public,
bbobj->keypairs + 60 * i, 40);
memcpy(&keypair[i + 1].private,
bbobj->keypairs + 60 * i + 40, 20);
}

if (globalinfo.verbose) {
fprintf(stderr, “\n”);
for (i = 0; i < numkeypairs; i++) {
fprintf(stderr,
“Public key %d x: “,
i + 1);
printMSBN(&keypair[i].public.x);
fprintf(stderr,
“\nPublic key %d y: “,
i + 1);
printMSBN(&keypair[i].public.y);
fprintf(stderr,
“\nPrivate key %d: “,
i + 1);
printMSBN(&keypair[i].private);
fprintf(stderr, “\n\n”);
}
}
} FreeLibrary(mylib);
} return 0;
}
static CONTKEY *checkLicense(wchar_t * license)
{
wchar_t *ebits = NULL;
wchar_t *pubkey = NULL;
wchar_t *value = NULL;
MS_BN *privkey = NULL;
CONTKEY *ckey = NULL;
MS_BN *thispubkey;
int i;

if ((ebits = get_element(L”ENABLINGBITS”, license)) == NULL)
error_exit(“No ENABLINGBITS element in license!”);

if ((pubkey = get_element(L”PUBKEY”, ebits)) == NULL)
error_exit(“No PUBKEY element in license!”);

if ((value = get_element(L”VALUE”, ebits)) == NULL)
error_exit(“No VALUE element in license!”);

MS_Base64Decode(pubkey, (char **) &thispubkey);
if (globalinfo.verbose) {
fprintf(stderr, “Checking license with PUBKEY “);
printMSBN(thispubkey);
fprintf(stderr, “\n”);
}
for (i = 0; i < numkeypairs; i++) {
if (memcmp(thispubkey, (char *) &keypair[i].public, 40) ==
0) {
privkey = &keypair[i].private;
break;
}
}

if (privkey != NULL) {
if ((ckey = malloc(sizeof(CONTKEY))) == NULL)
error_exit
(“Memory allocation failed in checkLicense”);
if (globalinfo.verbose) {
fprintf(stderr,
“Matched public key! Proceeding…\n”);
} MSDRM_setup(privkey, value, ckey);
}

free(thispubkey);
free(value);
free(pubkey);
free(ebits);

return ckey;
}
static CONTKEY *getContKey(wchar_t * licFile, wchar_t * kid)
{
HRESULT hr;
IStorage *pStg = NULL, *pStgChild = NULL;
IStream *pStrmLicense;
IEnumSTATSTG *penum = NULL;
STATSTG statstg, statstrm;
wchar_t *license = NULL;
unsigned long statLen, reallyRead;
CONTKEY *ckey;

hr = StgOpenStorage(licFile, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0,
&pStg);

if (FAILED(hr))
error_exit(“Couldn’t open license file!”);

hr = pStg->lpVtbl->OpenStorage(pStg, kid, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE,
NULL, 0, &pStgChild);

if (FAILED(hr))
return NULL;

hr = pStgChild->lpVtbl->EnumElements(pStgChild, 0, NULL, 0,
&penum);
if (FAILED(hr))
error_exit(“Couldn’t EnumElements in storage.”);

memset(&statstg, 0, sizeof(statstg));
hr = penum->lpVtbl->Next(penum, 1, &statstg, 0);
while (S_OK == hr) {
hr = pStgChild->lpVtbl->OpenStream(pStgChild,
statstg.pwcsName, NULL,
STGM_READ |
STGM_SHARE_EXCLUSIVE, 0,
&pStrmLicense);

if (FAILED(hr))
error_exit(“Couldn’t open license!”);

hr = pStrmLicense->lpVtbl->Stat(pStrmLicense, &statstrm,
0);

statLen = (unsigned long) statstrm.cbSize.QuadPart / 2;
if ((license =
(wchar_t *) malloc(2 * (statLen + 1))) == NULL)
error_exit
(“Memory allocation failed in getContKey.”);
hr = pStrmLicense->lpVtbl->Read(pStrmLicense, license,
2 * statLen, &reallyRead);

if ((FAILED(hr)) || (reallyRead != 2 * statLen))
error_exit(“License read failed.”);

license[statLen] = 0;

pStrmLicense->lpVtbl->Release(pStrmLicense);

if ((ckey = checkLicense(license + 1)) != NULL) {
free(license);
pStgChild->lpVtbl->Release(pStgChild);
pStg->lpVtbl->Release(pStg);
return ckey;
}

free(license);

hr = penum->lpVtbl->Next(penum, 1, &statstg, 0);
}

return NULL;
}
static void convertKID(wchar_t * kid)
{
while (*kid != L’\0′) {
if (*kid == L’/’)
*kid = L’@’;
else if (*kid == L’!’)
*kid = L’%’;
kid++;
}
}
CONTKEY *MSDRM_init(wchar_t * kid)
{
CONTKEY *ckey;
wchar_t *licfile;

licfile = getDRMDataPath();
if (licfile == NULL)
error_exit(“Couldn’t get DRM data path from registry.”);

wcscat(licfile, L”\\drmv2.lic”);

if (globalinfo.verbose) {
fprintf(stderr, “License file full path: “);
printwcs(licfile);
fprintf(stderr, “\n”);
}

getkeypairs();

convertKID(kid);
ckey = getContKey(licfile, kid);
if (ckey == NULL)
error_exit
(“Couldn’t find a valid license for this content.”);

free(licfile);

return ckey;
}
Viewing message <eed9abd18b5c46b1c1cfc8d2064b057f@noisebox.remailer.org>
From: Anonymous (nobody@noisebox.remailer.org)
Subject: FreeMe README file
Newsgroups: sci.crypt
Date: 2001-10-18 16:26:19 PST
—–BEGIN PGP SIGNED MESSAGE—–
The software distributed with this README file removes content
protection from any Windows Media Audio file (.wma file) that uses DRM
version 2 (as implemented in Windows Media Player version 7). It has
been tested under Windows 98, so may or may not work with other Media
Player/OS combinations. Also be aware that many “protected content”
files out there are actually DRM version 1, especially if they are
older files. This software will not do anything to unprotect version
1 files.

There is another piece of software, called “unfuck”, which similarly
removes protections, but there is a fundamental difference in how
these two pieces of software work. Unfuck works by allowing the
player to unprotect and uncompress the audio, and then captures the
audio samples on the way to the sound card. This software attacks the
problem directly: it simply removes the encryption from the protected
file, leaving everything else exactly the same. Because of this,
there is no loss of quality due to uncompressing and re-compressing
the content — what you’re left with is exactly the original content,
just not protected. It’s also much faster.

Please be aware that this software is “proof-of-concept” or
“demonstration” level, not production level. While every effort has
been made to make sure that it works properly, it may fail in
unforeseen situations — it has NOT been thoroughly tested! There
isn’t much chance that this program will screw up your system, but use
at your own risk!

WARNING!!!!! I have just learned that the new Microsoft Media
Player EULA includes a clause that says they can *automatically*
modify the software on your system, without any confirmation from
you required! In other words, they can disable your software, or
force an upgrade so that FreeMe won’t work, just because they feel
like it. Be careful out there!
CONTENTS:
README – This file
LICENSE – Yes, a license! Read it!
Technical – Full technical description of how MS DRMv2 works
Philosophy – My philosophy on why I released this, and what’s wrong
with the DMCA
FreeMe.exe – The actual program
src/ – The source code

The first 4 files can possibly be widely re-distributed and mirrored
without much fear of real legal worries — however, you will almost
certainly be harassed by several big and powerful companies, so keep
that in mind. The last two files (the program) cause more problems:
distributing these in the U.S. is almost certainly a violation of
the DMCA. However, outside the U.S. should be mostly ok — so
mirror these on as many foreign sites as possible! Again, you may
be harassed by big and powerful companies, and might get threatening
letters from lawyers, so be prepared for that.
INSTALLATION:
There’s just a single executable file “FreeMe.exe” to install. You
can copy it so that it’s in your executable PATH (for example, copy
to directory C:\WINDOWS\COMMAND), or you can put a shortcut to it on
your desktop — see below.
USAGE:
This is a command-line program, and the best way to run it is from
the command line. If it is installed, and the executable is in your
PATH, all you have to do is type “freeme x.wma” at a DOS prompt in
order to unprotect the file “x.wma”. There is a verbose flag that
you can invoke to have it print out all sorts of information as it
discovers it (your public/private key pairs, KID of the file you’re
unprotecting, content key, etc.). For example, typing “freeme -v
x.wma” unprotects the file as above, but in verbose mode. The
output file will be called “Freed-x.wma”, where “x.wma” is the
original filename.

One problem with this being a command line utility is that many
audio files have very long file names, so you’ll have to put the
filename in quotes in order for this to work, like so:
Prompt> freeme “Billy and the Boingers – The RIAA Stole My Soul.wma”

As an alternative, you can put a shortcut to the FreeMe.exe
executable on your desktop, and then can simply drag files from the
file explorer to FreeMe. However, there is one big problem with
this: the filename given to FreeMe is actually the short filename,
so if you did this with the file above, you’d end up with an output
file named something like “Freed-BILLYA~1.WMA” Unfortunately, I
don’t know how to fix this — maybe someone else out there does.
SOURCE CODE:
The full source code is included in the src/ directory, but you will
need a Win32 version of the OpenSSL package (and crypto library) in
order to compile it.

There are some definite problems with this code, which I would
suggest to people interested in improving what I’ve distributed:
First off, a lot of things in Windows seem to be designed to be
easier in C++ than in C; unfortunately, I don’t know a lot about
Windows programming, and never have learned or used C++ at all, so
some of my code may be a little strange in its approach. Secondly,
my .asf/.wma file format processing is hopelessly naive. Surely
there are better ways to do this, or existing libraries to do it.
Also, I don’t really do XML parsing, but just a very simplistic
scanning. This seems to work for every license I’ve seen, but using
a real XML parser would make this much more robust. Finally, a nice
pretty GUI would be good, but wasn’t necessary for my
“proof-of-concept” code, so I didn’t include it.

Finally, you know that Microsoft is going to make some changes that
will render my software useless. You’ve got the source code, so use
that as a starting point to change with them.
HOW TO CONTACT ME:
Being anonymous, it’s hard to give a way to contact me. However, if
you have something very important to tell me, post to the sci.crypt
Usenet newsgroup with a subject that includes the phrase “To Beale
Screamer”. My PGP key is given in this distribution, in case you
need it (and I will always sign anything I distribute).

Please don’t inundate the poor people in sci.crypt with a bunch of
pointless comments. But I did want to give people some avenue for
contacting me if absolutely necessary.
MESSAGES:
I have included messages below for specific groups of people.

Users: Please respect the uses I have intended this software for. I
want to make a point with this software, and if you use it for
purposes of violating copyrights, the message stands a very good
chance of getting lost. Also, Microsoft is obviously going to
release patches to their media player in order to get around the
exploit in my software — I think you’ll be safe it you refuse
to upgrade from your current version of the Windows Media Player
(but see the warning above about “forced upgrades”). Unless they
want to sacrifice backward compatibility, you will at least be
able to work with your current (legally obtained) media files
for the near future.

Microsoft: You guys have put together a pretty good piece of
software. Really. The only real technical flaw is that
licenses can’t be examined for their restrictions once they are
obtained. My real beef is with the media publishers’ use of this
software, not the technology itself. However, it’s easy to see
where software bloat and inefficiency comes from when this code
is examined: every main DLL has a separate copy of the elliptic
curve and other basic crypto routines, and parameters passed
back and forth between modules are encrypted giving unnecessary
overhead, not to mention all the checks of the code integrity,
checks for a debugger running, code encryption and decryption.
Perhaps you felt this was necessary for the “security through
obscurity” aspect, but I’ve got to tell you that this really
doesn’t make a bit of difference. Make lean and mean code,
because the obscurity doesn’t work as well as you think it does.
Also read the message below to the Justice Department!

Justice Department: Maybe this should really be addressed to the
state officials, since it looks like the current U.S.
administration doesn’t care too much about monopoly powers being
abused. But for whoever is interested, there is a very serious
anti-competitive measure in this software. In particular, for
various modules of the software to be used, you must supply a
certified public key for communication. Guess who controls the
certification of public keys? Microsoft. So if someone wants
to make a competing product, which integrates well with the
Windows OS, you will need to get Microsoft’s permission and
obtain a certificate from them. I don’t know what their policy
is on this, so don’t know if this power will be abused or not.
However, it has the potential for being a weapon Microsoft can
use to knock out any competition to their products.

Artists: Don’t fear new distribution methods — embrace them.
Technology is providing you the means to get your art directly
to consumers, avoiding the big record companies. They want a
piece of the action for YOUR creativity, and you don’t need to
let them in on it any more. Your fans will treat you nicely,
unless you treat your fans poorly (take note of that Lars). Bo
Diddley didn’t have anything to fear from his fans, but a lot to
fear from Leonard Chess. Think about that.

Publishers: Give us more options, not fewer. If you try to take
away our current rights, and dictate to us what we may or may
not do, you’re going to get a lot of resistance. You better
find a way to play nicely soon, because technology is making it
possible for artists to make do without you at all. Try getting
some progressive thinkers into management — current people
don’t seem to be able to cope with the new environment that is
emerging.

– – —

Original Distribution Date: October 18, 2001
by “Beale Screamer”
—–BEGIN PGP SIGNATURE—–
Version: 2.6.2

iQCVAwUBO84IDZCr1f2GXCalAQGRcAP8CHkU6B42NZNiuhS/roxKJTljm36Doq+R
zrqFeO2JY9xMCMhBlYP6RRkDATdlMWNj/U3DLXJ/lBJYUeSwMT3vsUTUHOA/lGMQ
9VqYHmAEwImnKWBNDG694abVeCFa9H/FziLLjeJQ73ADcfjr4rJ/FpHMxrtb2YfF
K5QaP3QRXl0=
=RK34
—–END PGP SIGNATURE—–

Viewing message <QDS2GL5737182.7715972222@anonymous.poster>
From: Anonymous (anonymous@anonymous.poster.com)
Subject: FreeMe source – main.c
Newsgroups: sci.crypt
Date: 2001-10-18 16:31:25 PST
Split into .c and .h files

——————— Start of main.c ———————-

/*
* FreeMe main.c — mostly a wma/asf file processor, with DRM part
* put off into msdrm.c
*/

#include <windows.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include “msdrm.h”

typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;

#pragma pack(1)
typedef struct chunk_save_st {
GUID guid;
uint64_t len;
void *data;
struct chunk_save_st *next;
} CHUNKSAVE;

typedef int (*GUIDHANDLER) (FILE * fp, CHUNKSAVE * savep);

typedef struct guidaction_st {
GUID *guid;
char *name;
GUIDHANDLER fn;
} GUIDACTION;

typedef struct fileheader_st {
GUID clientGUID;
uint64_t filesize;
uint64_t fileCreateTime;
uint64_t numPackets;
uint64_t timeAtEnd;
uint64_t playDuration;
uint32_t timeAtStart;
uint32_t unknown1;
uint32_t unknown2;
uint32_t packetSize;
uint32_t packetSize2;
uint32_t uncompressedSize;
} FILEHEADER;

int handle_chunk(FILE * fp, CHUNKSAVE * chunk);
int handle_header(FILE * fp, CHUNKSAVE * chunk);
int handle_file_header(FILE * fp, CHUNKSAVE * chunk);
int handle_data(FILE * fp, CHUNKSAVE * chunk);
int handle_stream_header(FILE * fp, CHUNKSAVE * chunk);
int handle_copy(FILE * fp, CHUNKSAVE * chunk);
int handle_drmv1(FILE * fp, CHUNKSAVE * chunk);
int handle_drmv2(FILE * fp, CHUNKSAVE * chunk);

struct globalinfo_st globalinfo;

GUID HeaderGUID = { 0x75b22630, 0x668e, 0x11cf, {0xa6, 0xd9,
0x00, 0xaa, 0x00, 0x62,
0xce, 0x6c}
};

GUID DataGUID = { 0x75b22636, 0x668e, 0x11cf, {0xa6, 0xd9,
0x00, 0xaa, 0x00, 0x62,
0xce, 0x6c}
};

GUID FileHeaderGUID = { 0x8cabdca1, 0xa947, 0x11cf, {0x8e, 0xe4,
0x00, 0xc0, 0x0c,
0x20, 0x53, 0x65}
};

GUID StreamHeaderGUID = { 0xb7dc0791, 0xa9b7, 0x11cf, {0x8e, 0xe6,
0x00, 0xc0, 0x0c,
0x20, 0x53, 0x65}
};

GUID AudioStreamGUID = { 0xf8699e40, 0x5b4d, 0x11cf, {0xa8, 0xfd,
0x00, 0x80, 0x5f,
0x5c, 0x44, 0x2b}
};

GUID Unknown1GUID = { 0x5fbf03b5, 0xa92e, 0x11cf, {0x8e, 0xe3,
0x00, 0xc0, 0x0c, 0x20,
0x53, 0x65}
};

GUID Unknown2GUID = { 0x86d15240, 0x311d, 0x11d0, {0xa3, 0xa4,
0x00, 0xa0, 0xc9, 0x03,
0x48, 0xf6}
};

GUID DRMv2HeaderGUID = { 0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35,
0xda, 0xe0, 0x7e,
0xe9, 0x28, 0x9c}
};

GUID DRMv1HeaderGUID = { 0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7,
0x00, 0xa0, 0xc9,
0x55, 0xfc, 0x6e}
};

GUID ContentDescrGUID = { 0x75b22633, 0x668e, 0x11cf, {0xa6, 0xd9,
0x00, 0xaa, 0x00,
0x62, 0xce, 0x6c}
};

GUID PropertyListGUID = { 0xd2d0a440, 0xe307, 0x11d2, {0x97, 0xf0,
0x00, 0xa0, 0xc9,
0x5e, 0xa8, 0x50}
};

GUIDACTION known_guids[] = {
{&HeaderGUID, “Header”, handle_header},
{&DataGUID, “Data”, handle_data},
{&FileHeaderGUID, “File Header”, handle_file_header},
{&StreamHeaderGUID, “Stream Header”, handle_copy},
{&Unknown1GUID, “Header subchunk – unknown 1”, handle_copy},
{&Unknown2GUID, “Header subchunk – unknown 2”, handle_copy},
{&DRMv2HeaderGUID, “DRMV2 ContentHeader”, handle_drmv2},
{&DRMv1HeaderGUID, “DRMv1 header”, handle_drmv1},
{&ContentDescrGUID, “Content Description”, handle_copy},
{&PropertyListGUID, “Property List”, handle_copy},
{NULL, NULL, NULL}
};
void error_exit(char *msg)
{
fprintf(stderr, “%s\n”, msg);
fprintf(stderr, “\n Press <ENTER> to acknowledge error.\n”);
getchar();
exit(1);
}

void printwcs(wchar_t * msg)
{
int len, rval;
char *buff;

len = wcslen(msg);
if ((buff = malloc(len + 1)) == NULL)
error_exit(“Memory allocation failed in printwcs”);

rval = WideCharToMultiByte(CP_ACP, 0, msg, len + 1, buff, len + 1,
NULL, NULL);
if (rval == 0)
error_exit(“WideCharToMultiByte failed in printwcs”);

fputs(buff, stderr);
free(buff);
}
GUIDHANDLER find_guid(GUID * guid)
{
GUIDHANDLER handler = NULL;
GUIDACTION *curr;

curr = known_guids;
while (curr->guid != NULL) {
if (memcmp(curr->guid, guid, sizeof(GUID)) == 0) {
handler = curr->fn;
break;
} curr++;
}

return handler;
}
int handle_copy(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long datalen = (unsigned long) (chunk->len – 24);

chunk->data = malloc(datalen);

if (chunk->data == NULL)
error_exit(“Memory allocation failed in handle_copy”);

if (fread(chunk->data, datalen, 1, fp) != 1) {
free(chunk->data);
chunk->data = NULL;
return 0;
}

return 1;
}
int handle_drmv1(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long toskip = (unsigned long) (chunk->len – 24);

fseek(fp, toskip, SEEK_CUR);
globalinfo.hasV1header = 1;
if (globalinfo.verbose)
fprintf(stderr, “Found DRMv1 header object.\n”);
return 1;
}
int handle_drmv2(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long datalen = (unsigned long) (chunk->len – 24);
unsigned short *data = malloc(datalen);

if (globalinfo.verbose)
fprintf(stderr, “Found DRMv2 header object.\n”);

if (data == NULL)
error_exit(“Memory allocation in handle_drmv2 failed.”);

fseek(fp, 6, SEEK_CUR);
if (fread(data, datalen – 6, 1, fp) != 1)
error_exit(“Data read in handle_drmv2 failed.”);

data[(datalen – 6) / 2] = L’\0′;

globalinfo.kid = get_element(L”KID”, data);
globalinfo.hasV2header = 1;

if (globalinfo.verbose) {
if (globalinfo.kid == NULL) {
fprintf(stderr,
“KID not found in header object!\n”);
} else {
fprintf(stderr, “Found KID (“);
printwcs(globalinfo.kid);
fprintf(stderr, “)\n”);
}
}

free(data);
return 1;
}
int handle_packet(FILE * fp, int packetlen)
{
struct packethead_st {
uchar id;
short unknown1;
uchar flags;
uchar segTypeID;
} *info;
uchar *data;
int flagoffset = 13;
int objlen;
int dataoffset;
int rval = 0;

if ((data = malloc(packetlen)) == NULL)
error_exit(“Memory allocation failed in handle_packet.”);

if (fread(data, packetlen, 1, fp) != 1)
goto exit;

info = (struct packethead_st *) data;
if (info->id != 0x82)
error_exit(“Unknown packet id – don’t know what to do!”);

if (info->flags & 0x40)
flagoffset += 2;

if (info->flags & 0x10)
flagoffset += 2;
else if (info->flags & 0x08)
flagoffset += 1;

if (info->flags & 0x01)
flagoffset += 1;

if (info->segTypeID == 0x55)
flagoffset += 1;
else if (info->segTypeID == 0x59)
flagoffset += 2;
else if (info->segTypeID == 0x5d)
flagoffset += 4;

if (data[flagoffset] != 8)
error_exit
(“Flag says grouping – don’t know how to do this!”);

dataoffset = flagoffset + 9;
if (info->flags & 0x01)
error_exit(“Need the data_length field – don’t know how!”);

objlen = *((int *) &data[flagoffset + 1]);

MSDRM_decr_packet(data + dataoffset, objlen,
globalinfo.content_key);

fwrite(data, packetlen, 1, stdout);
rval = 1;

exit:
free(data);
return rval;
}
int handle_data(FILE * fp, CHUNKSAVE * chunk)
{
struct datahead_st {
GUID unknownGUID;
uint64_t numPackets;
uchar unknown[2];
} datahead;
int packetcount = 0;
int lastpercent = -1;

if (fread(&datahead, sizeof(datahead), 1, fp) != 1)
return 0;

fwrite(chunk, 24, 1, stdout);
fwrite(&datahead, sizeof(datahead), 1, stdout);

if (globalinfo.verbose) {
fprintf(stderr, “Starting to process data packets\n”);
fprintf(stderr, “%d packets of length %d\n”,
globalinfo.numpackets, globalinfo.packetlen);
}

while (handle_packet(fp, globalinfo.packetlen)) {
packetcount++;
if (globalinfo.numpackets != 0) {
int percent, i;
percent =
((packetcount * 200) / globalinfo.numpackets +
1) / 2;
if (percent != lastpercent) {
fprintf(stderr, “|”);
for (i = 0; i < percent / 2; i++)
fprintf(stderr, “#”);
for (; i < 50; i++)
fprintf(stderr, ” “);
fprintf(stderr, “| “);
fprintf(stderr, “%3d%%\r”, percent);
lastpercent = percent;
}
}
}
fprintf(stderr, “\n”);

return 0;
}
int handle_header(FILE * fp, CHUNKSAVE * chunk)
{
struct header_st {
int numchunks;
short unknown;
} header;
int i;
CHUNKSAVE *subchunk = NULL;
int savecount = 0;
CHUNKSAVE *head = NULL, *tail = NULL;
FILEHEADER *fileheader;
uint64_t bytesremoved = 0;

if (fread(&header, sizeof(header), 1, fp) != 1)
return 0;

for (i = 0; i < header.numchunks; i++) {
if (subchunk == NULL) {
if ((subchunk = malloc(sizeof(CHUNKSAVE))) == NULL)
error_exit
(“Memory allocation failed in handle_header”);
}
if (!handle_chunk(fp, subchunk))
return 0;
if (subchunk->data != NULL) {
if (tail == NULL)
head = subchunk;
else
tail->next = subchunk;
subchunk->next = NULL;
tail = subchunk;

if ((subchunk = malloc(sizeof(CHUNKSAVE))) == NULL)
error_exit
(“Memory allocation failed in handle_header”);

savecount++;
} else { bytesremoved += subchunk->len;
}
}

if (globalinfo.fileheader == NULL) {
error_exit(“Didn’t see file header!”);
} else {
CHUNKSAVE *currchunk, *nextchunk;

if (!globalinfo.hasV2header) {
if (globalinfo.hasV1header)
error_exit
(“This file is version 1 protected, not version 2.”);
else
error_exit
(“This file doesn’t seem to be protected!”);
}

if (!globalinfo.kid)
error_exit
(“Version 2 protected, but no KID found!”);

if (globalinfo.verbose)
fprintf(stderr, “Starting to look for license.\n”);

globalinfo.content_key = MSDRM_init(globalinfo.kid);
if (globalinfo.content_key == NULL)
error_exit(“Couldn’t find a valid license!”);

if (freopen(globalinfo.ofname, “wb”, stdout) == NULL)
error_exit(“Couldn’t open output file.”);

if (globalinfo.verbose)
fprintf(stderr, “Opened output file <%s>\n”,
globalinfo.ofname);

currchunk = head;
fileheader =
(FILEHEADER *) ((CHUNKSAVE *) globalinfo.fileheader)->
data;
fileheader->filesize -= bytesremoved;
globalinfo.packetlen = fileheader->packetSize;
globalinfo.numpackets = (int) fileheader->numPackets;
header.numchunks = savecount;
chunk->len -= bytesremoved;
fwrite(chunk, 24, 1, stdout);
fwrite(&header, sizeof(header), 1, stdout);
while (currchunk != NULL) {
fwrite(currchunk, 24, 1, stdout);
fwrite(currchunk->data,
(unsigned long) (currchunk->len – 24), 1,
stdout);
nextchunk = currchunk->next;
free(currchunk->data);
free(currchunk);
currchunk = nextchunk;
}
}

return 1;
}
int handle_file_header(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long datalen = (unsigned long) (chunk->len – 24);

if ((chunk->data = malloc(datalen)) == NULL)
error_exit
(“Data allocation failed in handle_file_header.”);

if (fread(chunk->data, datalen, 1, fp) != 1) {
free(chunk->data);
chunk->data = NULL;
return 0;
}

globalinfo.fileheader = chunk;

return 1;
}
int handle_chunk(FILE * fp, CHUNKSAVE * chunk)
{
GUIDHANDLER handler = NULL;
int retval = 0;

chunk->data = NULL;

if (fread(&chunk->guid, sizeof(chunk->guid), 1, fp) != 1)
return 0;

if (fread(&chunk->len, sizeof(chunk->len), 1, fp) != 1)
return 0;

handler = find_guid(&chunk->guid);

if (handler != NULL) {
retval = (handler) (fp, chunk);
} else {
long toskip = (long) (chunk->len – 24);
retval = (fseek(fp, toskip, SEEK_CUR) == 0);
retval = 1;
}

return retval;
}
int main(int argc, char *argv[])
{
CHUNKSAVE chunk;
int more = 1;
FILE *ifp;
char *fnamestart;
static char ofname[1000];

globalinfo.verbose = 0;
globalinfo.fileheader = NULL;
globalinfo.kid = NULL;
globalinfo.hasV1header = 0;
globalinfo.hasV2header = 0;
globalinfo.ofname = ofname;

if ((argc < 2) || (argc > 3))
error_exit(“Usage: FreeMe [-v] protectedfile”);

if ((strcmp(argv[1], “-v”) != 0) && (argc == 3))
error_exit(“Usage: FreeMe [-v] protectedfile”);

if (argc == 3)
globalinfo.verbose = 1;

if ((ifp = fopen(argv[argc – 1], “rb”)) == NULL) {
sprintf(ofname, “Couldn’t open input file (%s)”,
argv[argc – 1]);
error_exit(ofname);
}

ofname[0] = ‘\0’;
if ((fnamestart = strrchr(argv[argc – 1], ‘\\’)) != NULL) {
memcpy(ofname, argv[argc – 1],
fnamestart – argv[argc – 1] + 1);
ofname[fnamestart – argv[argc – 1] + 1] = ‘\0’;
}
strcat(ofname, “Freed-“);
strcat(ofname, (fnamestart ? fnamestart + 1 : argv[argc – 1]));

while (more)
more = handle_chunk(ifp, &chunk);

return 0;
}

______________________________________________________________________________
Posted Via Binaries.net = SPEED+RETENTION+COMPLETION = http://www.binaries.net

The entire concept behind anonymity empowering our community comes from one manifesto. One personage – Beale.

Thanks Beale.

Citing the abuse of monopoly power and the Digital Millennium Copyright Act (DMCA) as his reason for developing the hack – we will just assume that the challenge and the ability to listen to music unsecured are also reasons – Beale Screamer posted this note to the Justice Department:
Justice Department: Maybe this should really be addressed to the state officials, since it looks like the current U.S. administration doesn’t care too much about monopoly powers being abused. But for whoever is interested, there is a very serious anti-competitive measure in this software [DRM]. In particular, for various modules of the software to be used, you must supply a certified public key for communication. Guess who controls the certification of public keys? Microsoft. So if someone wants to make a competing product, which integrates well with the Windows OS, you will need to get Microsoft’s permissi it has the potential for being a weapon Microsoft can use to knock out any competition to their products.on and obtain a certificate from them. I don’t know what their policy is on this, so don’t know if this power will be abused or not. However,

***Note from Ch1x0r***
(someone is making assumptions here…)

Beale Screamer goes into even more detail on <b>his</b> essay “Mad as Hell about the DMCA” which he includes with the FreeMe download on a file called “Philosophy”. There under the heading How Things Should Be Beale Screamer writes:

COPY:
23 October 2001
Source: Google archive of sci.crypt.

These are the messages Beale Screamer posted anonymously to newsgroup sci.crypt. Arranged chronologically here, but dates and times were probably assigned by the anonymous remailers.

Any messages missed, please tell: jya@pipeline.com

Microsoft DRM – Technical description, 2001-10-18 09:10:41 PST

FreeMe source – Multiswap.c,h, 2001-10-18 15:34:26 PST

FreeMe source – ecc.c,h, 2001-10-18 15:40:26 PST

FreeMe source – msdrm.c,h, 2001-10-18 16:20:33 PST

FreeMe README file, 2001-10-18 16:26:19 PST

FreeMe source – main.c, 2001-10-18 16:31:25 PST
Viewing message <17620O8I37182.4630787037@anonymous.poster>
From: Anonymous (anonymous@anonymous.poster.com)
Subject: Microsoft DRM – Technical description
Newsgroups: sci.crypt
Date: 2001-10-18 09:10:41 PST
—–BEGIN PGP SIGNED MESSAGE—–

Microsoft’s Digital Rights Management Scheme – Technical Details

By “Beale Screamer”

This document describes version 2 of the Microsoft Digital Rights

Management (MS-DRM), as applied to audio (.wma files). The sources

for this material are varied, and some of the information might be

slightly incomplete; however, the fundamental ideas are solid and

easily verified. There is no attempt at describing the older version

1 of DRM. While version 1 is widely used (probably more widely than

version 2!), and the scheme is somewhat simpler, the purpose of this

is to describe the latest technology and not necessarily allow all

existing systems to be broken. The ideas described here are also

implemented in the software originally distributed with this document

(but as an independent piece, so the software may or may not be

available from where you have obtained this document), so a real

implementation can be examined. Not all of the information here is

needed in order to write the software that removes the encryption, but

some of the more interesting points surrounding the MS-DRM scheme and

software are given even if not necessary. Also note that no code is

included in this document, either real code or pseudo-code. All

that’s in this document is a straight mathematical discussion, which

should be fully protected under the 1st Amendment to the

U.S. Constitution. I have no doubt that the corporate entities that

this document offends will attempt to suppress it, but I don’t think

any argument they make could hold up to Constitutional scrutiny.

The basic components of MS-DRM involve use of elliptic curve

cryptography (ECC) for public key cryptography, DES for a block

cipher, RC4 for a stream cipher, and SHA-1 for a hash function. There

is also a block cipher which I haven’t seen before, used in the MS-DRM

system to build a MAC, or keyed hash function. This cipher will be

explained completely below, and while the remaining algorithms are

well-known, more will be said about Microsoft’s use of ECC below.

In the discussion and examples below, all numbers are expressed in

hexadecimal in the standard ordering (most-to-least significant)

unless otherwise stated. The actual bytes comprising large numbers in

any code are stored little endian, so at times it is convenient to

look at data in that ordering, and this will be clearly marked when it

is done.

One confusing item is that binary data sent back and forth is encoded

using Base64, but not using the standard algorithm! For some reason,

Microsoft has decided to use the non-alphanumeric character ‘*’

instead of ‘/’, and ‘!’ instead of ‘+’ in some places, and in other

places they replace ‘/’ with ‘@’ and ‘!’ with ‘%’. This means that

any software dealing with these strings cannot use a standard Base64

decoder, but must use a custom-build decoder.

FILES INVOLVED

Several key DLLs are kept in \windows\system that relate to the MS-DRM

scheme.

drmv2clt.dll: Provides basic DRM version 2 functionality

blackbox.dll: Provides basic, machine-specific crypto for MS-DRM.

Functionality replaced by IndivBox.key when the local

system has been “individualized.”

The other interesting place for files is in \windows\All Users\DRM

(this location is not necessarily fixed but comes from the registry

entry HKEY_LOCAL_MACHINE\Software\Microsoft\DRM\DataPath). Here’s a

sampling of some of the files in this directory (these are hidden

system files, so be sure to turn on “view all files” in order to see

them!):

IndivBox.key: Despite the extension, this is really a DLL

that is an “individualized” version of blackbox.dll

drmv2.lic: The file of licenses (a structured IStorage file)

drmv2.sst: “Secure state” for each of the licenses. Also an IStorage

file, but each stream is RC4 encrypted.

v2ks.bla: The version 2 “key store” – this is where all the public/private

keys are kept (encrypted, of course!).

v2ksndv.bla: The individualized version 2 “key store.”

A SIMPLE BLOCK CIPHER (MULTISWAP)

Microsoft is using a very simple block cipher to create a message

authentication code (MAC). As this is not a standard algorithm, I

will describe it fully. The main operations in this cipher are

32-bit multiplications and swaps of the two halves of 32-bit words, so

I have called this cipher the “MultiSwap” cipher.

The MultiSwap cipher works on 64-bit blocks, using a key that consists

of 12 32-bit words, and a current state (or initialization vector)

that is 64-bits long. In the Microsoft implementation, the least

significant bits of all 12 words are set to 1, although once the

cipher is understood it is clear that really only 10 of the words

require this bit to be set. The basic operation of the cipher is a

transformation that is done on the first 32-bit word of the plaintext

block using the first 6 key words, and then repeated for the other

plaintext word and remaining key words.

Let k[0], k[1], k[2], k[3], k[4], and k[5] denote the first 6 key

words, and let s[0] and s[1] denote the two words of the state. To

transform a 32-bit input word x, first define the following function

f(a)=swap(swap(swap(swap(swap(a*k[0])*k[1])*k[2])*k[3])*k[4]) + k[5]

where * is multiplication modulo 2^(32), and “swap” is an operation

which exchanges the two 16-bit halves of a 32-bit word. The complete

transformation of a 32-bit word x then consists of s[1]=s[1]+f(x+s[0])

and s[0]=f(x+s[0]). This is first done with the value x set to the

first 32 bits of the input, and then repeated with x set to the second

32 bits and the using keys k[6] through k[11]. The output of the

block cipher is the new state s[0] and s[1].

The reason this block cipher can be inverted is because all the key

words are odd, which means they have multiplicative inverses modulo

2^(32). To invert f(), just do the operations in the reverse order:

first subtract off k[5], then do the multiply/swap operations with the

inverses of k[4] through k[0]. Notice that only the multiplicative

key words really need to be odd, so there is no reason for the least

significant bit of k[5] or k[11] to be set; however, Microsoft sets

these bits anyway.

This block cipher is never used for encryption, but is used to create

a message authentication code (MAC) in the standard way. Assuming the

length of the message to be hashed is a multiple of 8 bytes (64 bits),

the cipher is initialized with a state of all zeros, and then used to

encrypt the entire data. The output of the last block (the final

state) is the MAC for that message. This is used in computing packet

keys to encrypt protected content by MS-DRM, as will be explained

later.

ELLIPTIC CURVE CRYPTOGRAPHY

For ECC, Microsoft is using an elliptic curve over Zp, where p is a

160 bit prime number (given below). The curve consists of the points

that lie on the curve y^2=x^3+ax+b, where the operations are done over

the field Zp and a and b are coefficients that are given below.

All values are represented as packed binary values: in other words, a

single value over Zp is encoded simply as 20 bytes, stored in little

endian order. A point on the elliptic curve is therefore a 40 byte

block, which consists of two 20 byte little endian values (the x

coordinate followed by the y coordinate). Here are the parameters for

the elliptic curve used in MS-DRM:

p (modulus): 89abcdef012345672718281831415926141424f7

coefficient a: 37a5abccd277bce87632ff3d4780c009ebe41497

coefficient b: 0dd8dabf725e2f3228e85f1ad78fdedf9328239e

generator x: 8723947fd6a3a1e53510c07dba38daf0109fa120

generator y: 445744911075522d8c3c5856d4ed7acda379936f

Order of curve: 89abcdef012345672716b26eec14904428c2a675

These constants are fixed, and used by all parties in the MS-DRM

system. The “nerd appeal” of the modulus is high when you see this

number in hexadecimal: it includes counting in the hexadecimal, as

well as the digits of fundamental constants e, pi, and sqrt(2).

In order to use this public key system, any user must have a

private/public key pair. Since the security of the system relies

pretty heavily on the private keys remaining secret (even from the

user of the system on which they reside), they are carefully hidden.

In fact, there are keys hidden in various files that are used,

including blackbox.dll, v2ks.bla, and IndivBox.key. For example, once

the player has been individualized, IndivBox.key is created, and there

are at least two keys embedded into this file: a 64-bit key used for

RC4, and a 160-bit private key for use in ECC. The ECC private key is

used as the basic client key (the corresponding public key is stored

unencrypted in the key store file, and used as the initial part of the

“client id” sent when requesting a license), and additional key pairs

are stored in part of the keystore file (v2ks.bla or v2ksndv.bla),

encrypted with the RC4 key.

These secret keys are stored in linked lists that contain 32 bits per

node (so the key as a whole is not in contiguous memory), interspersed

with the code in the library (IndivBox.key for example). The idea is

that they can be read by that library, used internally by that

library, and never communicated outside the library. Since the

IndivBox.key file is shuffled in a random way for each client, these

keys would be extremely difficult to extract from the file itself.

Fortunately, we don’t have to: these keys are part of the object state

that is maintained by this library, and since the offset within this

object of these secret keys is known, we can let the library itself

extract the secret keys! The code for this simply loads up the “black

box” library, has it initialize an instance of the object, and then

reads the keys right out of that object. This is clearly a weakness

in the code which can be corrected by the DRM software fairly easily,

but for now it is the basis of our exploit.

GETTING A LICENSE

Each protected media file is encrypted with a “content key” that will

unlock the packets of the media stream. We describe briefly how a

license (containing a content key) is obtained for information

purposes, but the license acquisition protocol is not really important

for unlocking that content. Simply use the MS Media player, have it

request and decrypt the licenses, store them in drmv2.lic, and then we

can extract them directly from that file.

A protected media file is apparently recognized by the presence of a

DRMV2 object in the .wma file header. This object has GUID

298ae614-2622-4c17-b935-dae07ee9289c, and contains an XML object 6

bytes into the data part of the object. Among other things, this

header contains a “KID” element identifying the key used to unlock the

content. The drmv2.lic file is then checked to see if a license with

this KID exists locally. If the license doesn’t exist, a license

request is formed, which sends an encrypted “client id” to the license

server. This is sent as a “challenge,” which consists of 168 bytes in

the MS-Base64 encoding. The first 80 bytes are two ECC points, which

make up an ECC encrypted random session key, and the remaining 88

bytes are the “client id” encrypted using RC4 and the session key.

The ECC encryption is done using a public key that seems to be fixed

for all clients, so it is safe to assume that this corresponds to a

private key that is common to all license servers and built in to that

side of the system (without access to the server side code, it was

impossible to find the corresponding private key).

After some interaction, the license comes back as mime type

application/x-drm-v2, as an escaped XML-encoded license in the

following format

<LICENSERESPONSE><LICENSE version=”x.x.x.x”>

…base64 encoded license… </LICENSE></LICENSERESPONSE>

where x.x.x.x is most likely “2.0.0.0”. To make things tricky for a

sniffer, the license is actually RC4 encrypted using the same session

key that was established by the client when sending the challenge.

The client then decrypts the license and stores it in the drmv2.lic

file.

GETTING THE CONTENT KEY

Getting the content key from a license is pretty easy once the client

knows what its public/private key pairs are, and has a copy of the

license obtained from drmv2.lic. The license entry is an XML object

with an element for “ENABLINGBITS”, which has sub-elements ALGORITHM

(which should have type “MSDRM”), PUBKEY, VALUE, and SIGNATURE. The

PUBKEY element should match one of the client’s public keys, or else

there a problem! The VALUE element is the ECC-encrypted content key,

which can be decrypted by the private key that corresponds to the

given PUBKEY.

The content key has a specific format: the y coordinate is ignored,

and when the x coordinate is written in storage order (little endian),

the first byte is the length of the content key (which may always be

7), which is followed by that many bytes of the content key. While

the content key is tied to the encoded media file (which may be common

to many users), the enabling bits value will be different for each

user, and tied to that user’s public/private keys. Because of this,

licenses are not transferable from one user to another, even though

the media files themselves are (the new user must obtain his own

license from the license server).

We go through an example now of finding a content key. In this

example, we have identified our public and private keys as the

following values:

Public key x: 1957f96f3327a25bba52166ad7fcc74087b9734b

Public key y: 8939e1b1ed988182d34d17ebbcb0e03a82d062e7

Private key: 757ff01b853496452eea0b0646c3a357a6f33509

We’re looking at a file RIAALuvsMe.wma, and find in the header the

following bit of XML:

<KID>nA67jM7dNGIUQIkP5v7hSQ==</KID>

The actual KID seems to be a Base64 encoding of a GUID, but it is

treated as a string (uninterpreted) by the software, so the origin

doesn’t seem to make much difference.

The license is inside the drmv2.lic file, which is a structured “DOC

file”, meaning it can be accessed through the IStorage and IStream

interfaces (and it can be browsed by the Microsoft Visual Studio “DOC

File Viewer” tool if you’re curious). The top level drmv2.lic file

has a lower-level IStorage object for each KID, which can contain a

set of licenses for each KID. In order to guarantee valid IStorage

names, the KID is first processed to change all ‘/’ characters to ‘@’,

and all ‘!’ to ‘%’. The names of the IStream objects containing the

licenses again look like Base64 encoded GUIDs, which turn out to be

the LID (license ID?) element stored in the license. This can be

verified once the license is obtained, but we’re not sure how to

generate LID’s from the content header information, and so can’t

directly open the appropriate LID stream. Instead, we simply

enumerate through all available streams for this KID, testing each one

for a PUBKEY element (see below) that we know. This is taken to be

the license for this content. While this is really just a guess as to

the proper workings, it seems to work fine in all our tests.

Inside the license we find the following XML (this has been formatted

so that it’s easier to look at – in the actual file this would all be

on one line).

<ENABLINGBITS>

<ALGORITHM type=”MSDRM”></ALGORITHM>

<PUBKEY type=”machine”>

S3O5h0DH*NdqFlK6W6InM2*5VxnnYtCCOuCwvOsXTdOCgZjtseE5iQ==

</PUBKEY>

<VALUE>

VEsbPedfwrybrpkg0fhoOfe5eB9ef0R7QTxgX7NbtMIFK!h*4Pk7ek

PUqlDIRqYwQkgCGE0r0qtQdCUYszT!b7XedCIpsApQjstaFmafahM=

</VALUE>

<SIGNATURE>

KpxCm6lSXH8dTPI359jToftSEuLiP9v*zpHAy!kDEhlYkw6mkfQzlg==

</SIGNATURE>

</ENABLINGBITS>

The SIGNATURE element above is just random garbage. We didn’t make a

real signature for this example (among other things, we don’t have a

certified public key, which would have to follow this in a real

license. Requiring such a signature keeps people from creating their

own licenses, since only those that have been issued valid

certificates can do so).

First look at the PUBKEY part. If this is run through a Base64

decoder (modified for the MS character set as described earlier) you

get the following binary values, shown below as a memory dump:

0000: 4B 73 B9 87 40 C7 FC D7 6A 16 52 BA 5B A2 27 33

0010: 6F F9 57 19 E7 62 D0 82 3A E0 B0 BC EB 17 4D D3

0020: 82 81 98 ED B1 E1 39 89

Notice how this is exactly our public key from above, stored in little

endian order! So this license is for our machine.

Next, take the VALUE element above, run it through a Base64 decoder,

and interpret the 80 byte result as 4 20-byte values stored in little

endian order. These four numbers are as follows:

Encrypted u.x: & 1f78b9f73968f8d12099ae9bbcc25fe73d1b4b54

u.y: & 7a3bf9e07fe82b05c2b45bb35f603c417b447f5e

v.x: & 18257450abd22b4d1802484230a646c850aad443

v.y: & 136a9f66165acb8e500ab0292274deb56ffe34b3

To decrypt this value, first we multiply the point u by our private

key, resulting in the point

x: 399c72d525a9b65b7543a3e3adc88ce0f6a38db5

y: 66cfa6bdbfbb93b906b22deb36792363d8e8adc2

and then subtract this point from v to get

x: c91590616b4b3707

y: 753e24e50d437e147b4998376f163dc27b639a7a

Since x is so short, we have almost certainly gotten our content key.

Writing in storage order (little endian), x is

0000: 07 37 4B 6B 61 90 15 C9

which means that the content key has length 7 (from the first byte),

and the actual key is the string of bytes 374B6B619015C9.

DECRYPTING THE CONTENT

The content encryption process is simpler to explain than decryption,

so we start with that. The content key is not used directly, but is

processed for several different uses. First, the content key is

hashed using the SHA-1 hashing algorithm, producing a 20-bit output.

The first 12 bytes of this output are used as an RC4 key, and a block

of 16 words (or 64 bytes) of zeros is encrypted. The least

significant bits of the first 12 words of this output are all set, and

are used as the MultiSwap key. The next 2 words are the encryption

in-whitening mask and the next 2 words are the encryption

out-whitening mask (this will be explained later). The last 8 bytes

of the original SHA-1 hash output are used as a DES key.

To encrypt the content so that packets can be accessed randomly (for

seeks), the content cannot be encrypted as one single stream.

However, to strengthen the cipher we also don’t want to re-use the

same key for every packet. To satisfy both of these goals, MS-DRM

uses the following scheme to encrypt a packet: First, the packet (with

size rounded down to a multiple of 8 bytes) is run through

MultiSwapMAC to produce a 64-bit MAC. For some reason, the 32-bit

halves of this MAC are swapped before further processing. Next, the

entire packet is RC4 encrypted using the swapped MAC as an 8-byte RC4

key. The 8-byte MAC (with swapped halves) is then run through a

“whitened DES” by first XORing with the in-whitening mask, then

running through DES (using the DES key from the last paragraph), and

then XORing the result with the out-whitening mask. The resulting 8

bytes are then placed in the final encrypted packet, overwriting the

last full 8-byte block (not the last 8 bytes of the packet, but

blocking the packet from the beginning into 8 byte pieces, and

overwriting the last full piece).

To decrypt such a packet, first locate the last full 8-byte block, run

it through the whitened DES decryption, and the result is used as an

RC4 key to decrypt the packet. This will produce the correct packet

except for 8 bytes: those in the position of the last full 8-byte

block are wrong, since they were overwritten in the last phase of the

encryption. However, by swapping the halves of the RC4 key, we have

the MAC for the original packet up to and including the original bytes

in this position. Since the MAC is actually created out of a block

cipher, we can recover the original 8 bytes as follows: run the entire

packet through MultiSwapMAC up to the block in question, but not

including it. This output is the next-to-last state seen by

MultiSwapMAC in the encryption MAC computation, and we just recovered

the final output of the MAC, so we can put these two values into the

block cipher decryption to obtain the original data of this 8-byte

block. The original 8-bytes are placed back into the packet, and now

the entire original contents are restored!

This is a pretty clever scheme: by using a MAC constructed from a

block cipher, individual packet keys can be computed and encoded into

the encrypted packet with absolutely no increase in space, and since

the size is maintained nothing in the structure of the content file

(describing packet sizes or other parameters) needs to be changed at

all. The encrypted content can be completely transparent to

applications that deal with .wma files in a non-content-sensitive

manner.

We finish this section showing an example of content decryption, using

the content key from the previous paragraph. We first process the

content key by running it through SHA-1 to obtain

15 CB 92 F9 97 2E C8 75 29 4F 12 65 36 B6 C6 DB AC A2 40 35

The first 12 bytes are used as an RC4 key to encrypt a block of all

zeros, giving

0000: 80 0A 2D 48 D1 FD 7E ED 83 69 4A 7D A5 D5 EE C4

0010: 4E E1 64 52 D1 71 98 26 9A F3 14 E3 51 C8 B6 92

0020: D4 93 E4 57 97 6D 63 EF 0E 06 07 54 F7 DD ED 38

0030: E8 CA A0 D0 83 13 F1 DB C1 70 AE 56 61 7D FB 94

The first 48 bytes are interpreted as 12 32-bit words, stored little

endian, and are saved as the MultiSwap key after setting all least

significant bits. So the key values are k[0]=0x482D0A81,

k[1]=0xED7EFDD1, etc. The last 16 bytes of the RC4-encrypted block

are the whitening masks for DES, and the key for DES is given as the

last 8 bytes of the SHA-1 hash value.

Assume we get a packet with size 1450 bytes, which is 181 8-byte

blocks followed by 2 additional bytes. Numbering the byte positions

as 0 through 1449, we look at the bytes in positions 1440 through 1447

(the last full 8-byte block), and find that they are

A8 49 65 36 A2 33 18 09

XORing with the encryption out-whitening mask (the last 8 bytes from

the RC4-encrypted block above) we get

69 39 CB 60 C3 4E E3 9D

and decrypting with DES using key 36 B6 C6 DB AC A2 40 35 gives

FD EF 98 7D 8B 77 72 FD

and finally XORing with the encryption in-whitening mask (the

next-to-last 8-byte piece in the RC4-encrypted block above) gives

15 25 38 AD 08 64 83 26

This is then used as an RC4 key to decrypt the packet. To replace

bytes at positions 1440 through 1447 with the correct values, take

the RC4 value and swap the words around to get

08 64 83 26 15 25 38 AD

This is the MultiSwapMAC of the input packet using bytes 0 through

1447. We run bytes 0 through 1439 through MultiSwapMAC to get

D9 F7 D9 53 A9 6E 14 D9

and then use this as the state input, along with the original MAC

output as the data input to the MultiSwapDecode function to obtain

DA 05 D8 EB 97 FE 1E 7B

These 8 bytes are placed in positions 1440 through 1447, and then the

entire original packet is restored.

OTHER ISSUES

Communication between different DLL modules is encrypted and checked

at multiple points. This works roughly as follows: Objects are

initialized with communication parameters by sending certified public

keys to the object you want to communicate with. The second object

verifies these certificates, generates a random session key (which it

uses to generate a MultiSwap key in addition to the use as a session

key), and sends the encrypted session key back to the calling object.

Future “sensitive communication” is RC4 encrypted with the session key,

and run through MultiSwapMAC to verify integrity (after padding with

zeros to make the data a multiple of 64 bits). This is done for data

sent both to and from the object.

Presumably, this is done so that anyone monitoring parameters passed

between DLL modules wouldn’t see any “sensitive data,” although its

use for this purpose is pretty limited. However, it does lead to some

interesting and strange situations: when blackbox is sent a packet to

decrypt, it decrypts it, and then immediately *re-encrypts* it using

the session key to send it back to the media player! So in decrypting

a packet, the computer actually goes through a decrypt/encrypt/decrypt

sequence of operations!

One very important effect of this scheme is that Microsoft fully

controls who gets to write modules that interact with the basic

Microsoft media modules. Without a certified public key (and the

corresponding private key) it is impossible to write a compatible DLL

that interfaces with their code. Since Microsoft controls the issuing

of certified public keys, they also have complete control over who is

allowed to make compatible and competing products. Microsoft’s

reputation for being generous to competitors is well-known, so this

effectively gives Microsoft a technically guaranteed monopoly power.

Of course, these certificates and private keys must be distributed

with any “Microsoft blessed” software as well, and in fact exist in

the media player and blackbox DLLs. They’re not hard to extract, if

you know where to look, but I won’t give them here. They would be of

limited use anyway, since Microsoft also has a “revocation list”

mechanism built in to the Media player software, meaning that they can

revoke any of these certificates at their whim, remotely disabling any

software that depends on that certificate for communication.

—–BEGIN PGP SIGNATURE—–

Version: 2.6.2

iQCVAwUBO5qt3JCr1f2GXCalAQE8ygP9Gb4Dm0ZQ5GePjAIfMFyqYVtUNSUUfj7A

3ZLwbMwUtnRHeYDGWRJEqvJMPf4SujKHcwQL3LtefrhH7dOn6r4AyUQV6ymezpd/

AMY53ONufawU+T8YgilEe2WCDRc4Y/uDbQFZIhcPQ+H78nzFSvdj+FzQ7pKrxsIr

QWe1ZNP4xfY=

=WL0q

—–END PGP SIGNATURE—–
______________________________________________________________________________
Posted Via Binaries.net = SPEED+RETENTION+COMPLETION = http://www.binaries.net

From: Frog2 (FrogRemailer@NoReply.Invalid.com)
Subject: FreeMe source – Multiswap.c,h
Newsgroups: sci.crypt
Date: 2001-10-18 15:34:26 PST
Split into .c and .h files

——————— Start of MultiSwap.h ———————-

#if !defined( _MULTISWAP_H_ )
#define _MULTISWAP_H_

typedef struct multiswapkey_st {
struct {
int multikey[5];
int multiinv[5];
int additive;
} round[2];
} MULTISWAPKEY;

void MultiSwapSetKey(MULTISWAPKEY * out, unsigned int *datain);
void MultiSwapEncode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout);
void MultiSwapMAC(MULTISWAPKEY * key, unsigned int *data, int num64bits,
unsigned int *out);
void MultiSwapDecode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout);

#endif

——————— Start of MultiSwap.c ———————-

#include “MultiSwap.h”
static int mod232inv(int x)
{
int rval = x;
int i;

for (i = 0; i < 30; i++)
rval *= rval * x;

return rval;
}
void MultiSwapSetKey(MULTISWAPKEY * out, unsigned int *datain)
{
int i;

for (i = 0; i < 5; i++) {
out->round[0].multikey[i] = datain[i] | 1;
out->round[0].multiinv[i] = mod232inv(datain[i] | 1);
out->round[1].multikey[i] = datain[6 + i] | 1;
out->round[1].multiinv[i] = mod232inv(datain[6 + i] | 1);
}

/* Note that the “| 1” part isn’t necessary here, since we don’t
* need multiplicative inverses. But they do it anyway!
*/

out->round[0].additive = datain[5] | 1;
out->round[1].additive = datain[11] | 1;
}
void MultiSwapEncode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout)
{
unsigned int cst = state[0];

cst += datain[0];
cst *= key->round[0].multikey[0];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multikey[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multikey[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multikey[3];
cst = (cst >> 16) | (cst << 16);
cst = cst * key->round[0].multikey[4] + key->round[0].additive;
dataout[1] = cst + state[1];

cst += datain[1];
cst *= key->round[1].multikey[0];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multikey[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multikey[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multikey[3];
cst = (cst >> 16) | (cst << 16);
cst = cst * key->round[1].multikey[4] + key->round[1].additive;
dataout[1] += cst;

dataout[0] = cst;
}
void MultiSwapMAC(MULTISWAPKEY * key, unsigned int *data, int num64bits,
unsigned int *out)
{
unsigned int state[2];
unsigned int newstate[2] = { 0, 0 };
int i;

for (i = 0; i < num64bits; i++) {
state[0] = newstate[0];
state[1] = newstate[1];
MultiSwapEncode(key, state, data + 2 * i, newstate);
}

out[0] = newstate[0];
out[1] = newstate[1];
}
void MultiSwapDecode(MULTISWAPKEY * key, unsigned int *state,
unsigned int *datain, unsigned int *dataout)
{
unsigned int cst = datain[0];
unsigned int tmp;

dataout[1] = datain[1] – cst;
cst = (cst – key->round[1].additive) * key->round[1].multiinv[4];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[3];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[1].multiinv[0];
tmp = dataout[1] – state[1];
dataout[1] = cst – tmp;
cst = tmp;

cst = (cst – key->round[0].additive) * key->round[0].multiinv[4];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[3];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[2];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[1];
cst = (cst >> 16) | (cst << 16);
cst *= key->round[0].multiinv[0];

dataout[0] = cst – state[0];
}

From: Nomen Nescio (nobody@dizum.com)
Subject: FreeMe source – ecc.c,h
Newsgroups: sci.crypt
Date: 2001-10-18 15:40:26 PST
Split into .c and .h files

——————— Start of ecc.h ———————-

#if !defined( _ECC_H )
#define _ECC_H

#include <openssl/bn.h>

/*
* If the “neg” field of the BIGNUM struct is set, then this point is
* the identity. This is a terrible way to do this, since it’s not
* clear what the future of this flag is — however, for now it works,
* and it’s fast…
*/

typedef struct eccpt_st {
BIGNUM *x, *y;
} ECCpt;

typedef struct eccparam_st {
BIGNUM *modulus; /* Curve is over Z_modulus */
BIGNUM *a, *b; /* Curve coefficients */
ECCpt generator; /* Generator for our operations */
ECCpt pubkey; /* Public key */
BIGNUM *privkey; /* Corresponding private key */
} ECC;
ECC *ECC_new_set(BIGNUM * p, BIGNUM * a, BIGNUM * b, ECCpt g);
void ECC_free(ECC * ecc);
void ECCpt_init(ECCpt * pt);
void ECCpt_free(ECCpt * pt);
int ECCpt_is_valid_pt(ECCpt * a, ECC * ecc);
int ECCpt_is_equal(ECCpt * a, ECCpt * b);
void ECCpt_add(ECCpt * r, ECCpt * a, ECCpt * b, ECC * ecc);
void ECCpt_mul(ECCpt * r, ECCpt * a, BIGNUM * n, ECC * ecc);

#endif

——————— Start of ecc.c ———————-

/* General purpose elliptic curve routines. */

#include <stdio.h>
#include <stdlib.h>
#include “ecc.h”
static int ECC_param_ok(BIGNUM * p, BIGNUM * a, BIGNUM * b)
{
BIGNUM *tmp1, *tmp2;
BN_CTX *ctx;
int retval;

if ((p == NULL) || (a == NULL) || (b == NULL))
return 0;

tmp1 = BN_new();
tmp2 = BN_new();
ctx = BN_CTX_new();

BN_mod_mul(tmp1, a, a, p, ctx);
BN_mod_mul(tmp1, tmp1, a, p, ctx);
BN_lshift(tmp1, tmp1, 2);
BN_mod(tmp1, tmp1, p, ctx);

tmp2 = BN_new();
BN_set_word(tmp2, 27);
BN_mod_mul(tmp2, tmp2, b, p, ctx);
BN_mod_mul(tmp2, tmp2, b, p, ctx);

BN_add(tmp2, tmp1, tmp2);
BN_mod(tmp2, tmp2, p, ctx);

retval = !BN_is_zero(tmp2);

BN_CTX_free(ctx);
BN_free(tmp1);
BN_free(tmp2);

return retval;
}
ECC *ECC_new_set(BIGNUM * p, BIGNUM * a, BIGNUM * b, ECCpt g)
{
ECC *ecc;

if (!ECC_param_ok(p, a, b))
return NULL;

ecc = malloc(sizeof(ECC));
if (ecc != NULL) {
ecc->modulus = BN_dup(p);
ecc->a = BN_dup(a);
ecc->b = BN_dup(b);
ecc->generator.x = BN_dup(g.x);
ecc->generator.y = BN_dup(g.y);
ecc->pubkey.x = ecc->pubkey.y = NULL;
ecc->privkey = NULL;
}

return ecc;
}
void ECC_free(ECC * ecc)
{
if (ecc != NULL) {
BN_free(ecc->modulus);
ecc->modulus = NULL;
BN_free(ecc->a);
ecc->a = NULL;
BN_free(ecc->b);
ecc->b = NULL;
BN_free(ecc->generator.x);
ecc->generator.x = NULL;
BN_free(ecc->generator.y);
ecc->generator.y = NULL;
if (ecc->pubkey.x != NULL) {
BN_free(ecc->pubkey.x);
ecc->pubkey.x = NULL;
BN_free(ecc->pubkey.y);
ecc->pubkey.y = NULL;
}
if (ecc->privkey != NULL) {
BN_free(ecc->privkey);
ecc->privkey = NULL;
} free(ecc);
}
}
void ECCpt_init(ECCpt * pt)
{
pt->x = BN_new();
pt->y = BN_new();
}
void ECCpt_free(ECCpt * pt)
{
BN_free(pt->x);
pt->x = NULL;
BN_free(pt->y);
pt->y = NULL;
}
int ECCpt_is_valid_pt(ECCpt * a, ECC * ecc)
{
/* check that y^2 = x^3 + a x + b */
BIGNUM *tmp1, *tmp2;
BN_CTX *ctx;
int retval;

ctx = BN_CTX_new();
tmp1 = BN_dup(a->x);
BN_mod_mul(tmp1, tmp1, tmp1, ecc->modulus, ctx);
BN_add(tmp1, tmp1, ecc->a);
BN_mod_mul(tmp1, tmp1, a->x, ecc->modulus, ctx);
BN_add(tmp1, tmp1, ecc->b);
if (BN_cmp(tmp1, ecc->modulus) >= 0)
BN_sub(tmp1, tmp1, ecc->modulus);

tmp2 = BN_dup(a->y);
BN_mod_mul(tmp2, tmp2, tmp2, ecc->modulus, ctx);

retval = (BN_cmp(tmp1, tmp2) == 0);
BN_free(tmp1);
BN_free(tmp2);
BN_CTX_free(ctx);
return retval;
}
int ECCpt_is_equal(ECCpt * a, ECCpt * b)
{
if (a->x->neg && b->x->neg)
return 1;
return ((BN_cmp(a->x, b->x) == 0) && (BN_cmp(a->y, b->y) == 0));
}
void ECCpt_add(ECCpt * r, ECCpt * a, ECCpt * b, ECC * ecc)
{
BN_CTX *ctx;
BIGNUM *tmp1, *tmp2;
BIGNUM *lambda;

if (a->x->neg) {
BN_copy(r->x, b->x);
BN_copy(r->y, b->y);
return;
}

if (b->x->neg) {
BN_copy(r->x, a->x);
BN_copy(r->y, a->y);
return;
}

tmp1 = BN_new();
if (BN_cmp(a->x, b->x) == 0) {
BN_add(tmp1, a->y, b->y);
if (BN_cmp(tmp1, ecc->modulus) == 0) {
BN_free(tmp1);
r->x->neg = 1; /* Set to identity */
return;
}
}

ctx = BN_CTX_new();
tmp2 = BN_new();
lambda = BN_new();
if (ECCpt_is_equal(a, b)) {
BN_set_word(tmp1, 3);
BN_mod_mul(tmp1, tmp1, a->x, ecc->modulus, ctx);
BN_mod_mul(tmp1, tmp1, a->x, ecc->modulus, ctx);
BN_add(tmp1, tmp1, ecc->a);
BN_mod(tmp1, tmp1, ecc->modulus, ctx);
BN_lshift1(tmp2, a->y);
BN_mod_inverse(tmp2, tmp2, ecc->modulus, ctx);
BN_mod_mul(lambda, tmp1, tmp2, ecc->modulus, ctx);
} else {
BN_sub(tmp1, b->x, a->x);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
tmp2 = BN_mod_inverse(NULL, tmp1, ecc->modulus, ctx);
BN_sub(tmp1, b->y, a->y);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
BN_mod_mul(lambda, tmp1, tmp2, ecc->modulus, ctx);
}

BN_mod_mul(tmp1, lambda, lambda, ecc->modulus, ctx);
BN_sub(tmp1, tmp1, a->x);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
BN_sub(tmp2, tmp1, b->x);
if (tmp2->neg)
BN_add(tmp2, ecc->modulus, tmp2);

BN_sub(tmp1, a->x, tmp2);
if (tmp1->neg)
BN_add(tmp1, ecc->modulus, tmp1);
BN_mod_mul(tmp1, lambda, tmp1, ecc->modulus, ctx);
BN_sub(r->y, tmp1, a->y);
if (r->y->neg)
BN_add(r->y, ecc->modulus, r->y);

BN_free(r->x);
r->x = tmp2;
tmp2 = NULL;

BN_free(lambda);
BN_free(tmp1);
BN_CTX_free(ctx);
}
void ECCpt_mul(ECCpt * r, ECCpt * a, BIGNUM * n, ECC * ecc)
{
ECCpt tmp;
int numbits, i;

tmp.x = BN_dup(a->x);
tmp.y = BN_dup(a->y);
r->x->neg = 1;
numbits = BN_num_bits(n);
for (i = numbits – 1; i >= 0; i–) {
if (BN_is_bit_set(n, i))
ECCpt_add(r, r, &tmp, ecc);
if (i > 0)
ECCpt_add(r, r, r, ecc);
}
}

Viewing message <c9e08269164a5978d72b12be3bc77a6c@remailer.segfault.net>
From: Anonymous Coredump (mixmaster@remailer.segfault.net)
Subject: FreeMe source – msdrm.c,h
Newsgroups: sci.crypt
Date: 2001-10-18 16:20:33 PST
Split into .c and .h files

——————— Start of msdrm.h ———————-

#if !defined( _MSDRM_H_ )
#define _MSDRM_H_

#include <openssl/des.h>
#include “ecc.h”
#include “MultiSwap.h”

typedef unsigned char uchar;

#define MS_BN_LEN 20

typedef struct ms_bn_st {
uchar d[MS_BN_LEN];
} MS_BN;

typedef struct ms_eccpt_st {
MS_BN x, y;
} MS_ECCpt;

typedef struct contkey_st {
uchar ckey[MS_BN_LEN];
int ckeylen;
uchar keyhash[20];
des_key_schedule keysched;
uchar outmask[8];
uchar inmask[8];
MULTISWAPKEY hashkey;
} CONTKEY;

wchar_t *get_element(wchar_t * tag, wchar_t * str);
void MSDRM_decr_packet(uchar * data, int len, CONTKEY * ckey);
CONTKEY *MSDRM_init(wchar_t * license);

extern struct globalinfo_st {
int verbose;
void *fileheader;
char *ofname;
int packetlen;
int numpackets;
CONTKEY *content_key;
wchar_t *kid;
int hasV1header;
int hasV2header;
} globalinfo;

#endif

——————— Start of msdrm.c ———————-

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#include <windows.h>
#include <ole2.h>
#include <openssl/sha.h>
#include <openssl/rc4.h>
#include “ecc.h”
#include “msdrm.h”

extern void error_exit(char *msg);
extern void printwcs(wchar_t * msg);

typedef struct bboxobj_st {
void *jtable;
void *jtbl2;
void *jtbl3;
MS_BN ecprivkey;
MS_ECCpt ecpt1;
uchar clientid[84]; /* First part is public key */
uchar hwid[20];
uchar rc4key[6];
uchar pad1[2];
int numkeypairs;
uchar *keypairs;
} BBOXOBJ;

#define MAXKEYPAIRS 50

struct keypair_st {
MS_ECCpt public;
MS_BN private;
} keypair[MAXKEYPAIRS];
int numkeypairs = 0;
static MS_BN msec_mod = {
{0xf7, 0x24, 0x14, 0x14, 0x26, 0x59, 0x41, 0x31, 0x18, 0x28,
0x18, 0x27, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x89}
};

static MS_BN msec_a = {
{0x97, 0x14, 0xe4, 0xeb, 0x09, 0xc0, 0x80, 0x47, 0x3d, 0xff,
0x32, 0x76, 0xe8, 0xbc, 0x77, 0xd2, 0xcc, 0xab, 0xa5, 0x37}
};

static MS_BN msec_b = {
{0x9e, 0x23, 0x28, 0x93, 0xdf, 0xde, 0x8f, 0xd7, 0x1a, 0x5f,
0xe8, 0x28, 0x32, 0x2f, 0x5e, 0x72, 0xbf, 0xda, 0xd8, 0x0d}
};

static MS_BN msec_gx = {
{0x20, 0xa1, 0x9f, 0x10, 0xf0, 0xda, 0x38, 0xba, 0x7d, 0xc0,
0x10, 0x35, 0xe5, 0xa1, 0xa3, 0xd6, 0x7f, 0x94, 0x23, 0x87}
};

static MS_BN msec_gy = {
{0x6f, 0x93, 0x79, 0xa3, 0xcd, 0x7a, 0xed, 0xd4, 0x56, 0x58,
0x3c, 0x8c, 0x2d, 0x52, 0x75, 0x10, 0x91, 0x44, 0x57, 0x44}
};
static void printMSBN(MS_BN * num)
{
int i;
for (i = MS_BN_LEN – 1; i >= 0; i–)
fprintf(stderr, “%02x”, num->d[i]);
}
static BIGNUM *MS_BN_to_BN(MS_BN * msnum, BIGNUM * r)
{
uchar bigendian[MS_BN_LEN];
int i;

for (i = 0; i < MS_BN_LEN; i++)
bigendian[i] = msnum->d[MS_BN_LEN – 1 – i];

return BN_bin2bn(bigendian, MS_BN_LEN, r);
}
static void MS_ECCpt_to_ECCpt(MS_ECCpt * mspt, ECCpt * r)
{
MS_BN_to_BN(&mspt->x, r->x);
MS_BN_to_BN(&mspt->y, r->y);
}
static ECC *MSECC_new_set()
{
BIGNUM *tmod, *ta, *tb;
ECC *ecc;
ECCpt tg;

tmod = BN_new();
ta = BN_new();
tb = BN_new();
ECCpt_init(&tg);

MS_BN_to_BN(&msec_mod, tmod);
MS_BN_to_BN(&msec_a, ta);
MS_BN_to_BN(&msec_b, tb);
MS_BN_to_BN(&msec_gx, tg.x);
MS_BN_to_BN(&msec_gy, tg.y);

ecc = ECC_new_set(tmod, ta, tb, tg);

BN_free(tmod);
BN_free(ta);
BN_free(tb);
ECCpt_free(&tg);

return ecc;
}
static void MSECC_set_privkey(MS_BN * pk, ECC * ecc)
{
if (ecc->privkey == NULL)
ecc->privkey = BN_new();
MS_BN_to_BN(pk, ecc->privkey);
}
static void BN_to_MS_BN(BIGNUM * in, MS_BN * out)
{
MS_BN tmp;
int bytelen, i;

bytelen = BN_num_bytes(in);
if (bytelen > MS_BN_LEN)
error_exit
(“Bug in code: Result is too big in BN_to_MS_BN”);

for (i = 0; i < MS_BN_LEN; i++)
tmp.d[i] = 0;

BN_bn2bin(in, (uchar *) & tmp.d[MS_BN_LEN – bytelen]);

for (i = 0; i < MS_BN_LEN; i++)
out->d[i] = tmp.d[MS_BN_LEN – 1 – i];
}
static void MSECC_decrypt(MS_ECCpt * r, MS_ECCpt * ctext, ECC * ecc)
{
ECCpt u, v;

if (ecc->privkey == NULL)
error_exit
(“Bug in code: MSECC_decrypt called with no private key!”);

ECCpt_init(&u);
ECCpt_init(&v);
MS_ECCpt_to_ECCpt(&ctext[0], &u);
MS_ECCpt_to_ECCpt(&ctext[1], &v);

ECCpt_mul(&u, &u, ecc->privkey, ecc);
BN_sub(u.y, ecc->modulus, u.y);
ECCpt_add(&v, &v, &u, ecc);

BN_to_MS_BN(v.x, &r->x);
BN_to_MS_BN(v.y, &r->y);

ECCpt_free(&u);
ECCpt_free(&v);
}
static int MS_Base64Decode(wchar_t * str, char **buff)
{
wchar_t *cp;
char *ocp;
int len, val, count, block, ocount;

len = wcslen(str);
if ((*buff = malloc((len * 3) / 4)) == NULL)
error_exit(“Memory allocation failed in MS_Base64Decode.”);

ocp = *buff;
count = 0;
block = 0;
ocount = 0;
for (cp = str; *cp != L’\0′; cp++) {
if ((*cp >= L’A’) && (*cp <= L’Z’))
val = *cp – L’A’;
else if ((*cp >= L’a’) && (*cp <= L’z’))
val = *cp – L’a’ + 26;
else if ((*cp >= L’0′) && (*cp <= L’9′))
val = *cp – L’0′ + 52;
else if ((*cp == L’+’) || (*cp == L’!’))
val = 62;
else if ((*cp == L’/’) || (*cp == L’*’))
val = 63;
else if (*cp == L’=’) {
if (count == 2) {
*ocp++ = block >> 4;
ocount++;
} else {
*ocp++ = (block >> 10);
*ocp++ = (block >> 2) & 0xff;
ocount += 2;
} break;
} else
val = -1;

if (val >= 0) {
block = (block << 6) | val;
if (++count == 4) {
*ocp++ = block >> 16;
*ocp++ = (block >> 8) & 0xff;
*ocp++ = block & 0xff;
ocount += 3;
count = 0;
}
}
}

return ocount;
}
void MSDRM_decr_packet(uchar * data, int len, CONTKEY * ckey)
{
RC4_KEY rc4state;
int num64bits = len / 8;
uchar work2[8];
int i;
unsigned int pustate[2];
unsigned int tmpd[2];
uchar *keystart = data + (num64bits – 1) * 8;

if (len < 16) {
for (i = 0; i < len; i++)
data[i] ^= ckey->keyhash[i];
return;
}

for (i = 0; i < 8; i++)
keystart[i] ^= ckey->inmask[i];

des_ecb_encrypt((const_des_cblock *) keystart,
(des_cblock *) work2, ckey->keysched, 0);

for (i = 0; i < 8; i++)
work2[i] ^= ckey->outmask[i];

RC4_set_key(&rc4state, 8, work2);
RC4(&rc4state, len, data, data);

MultiSwapMAC(&ckey->hashkey, (unsigned int *) data, num64bits – 1,
pustate);
tmpd[0] = ((int *) work2)[1];
tmpd[1] = ((int *) work2)[0];
MultiSwapDecode(&ckey->hashkey, pustate, tmpd,
(unsigned int *) keystart);
}
static void MSDRM_setup(MS_BN * privkey, wchar_t * value, CONTKEY * out)
{
ECC *msecc;
MS_ECCpt dec;
RC4_KEY rc4state;
uchar rc4buff[64];
int len;
char *dynbuff;

msecc = MSECC_new_set();
MSECC_set_privkey(privkey, msecc);

len = MS_Base64Decode(value, &dynbuff);
MSECC_decrypt(&dec, (MS_ECCpt *) dynbuff, msecc);
free(dynbuff);

ECC_free(msecc);
msecc = NULL;

if ((uchar) dec.x.d[0] > MS_BN_LEN – 1)
error_exit(“Decrypted content key is too big!”);

out->ckeylen = (uchar) dec.x.d[0];
memcpy(out->ckey, &dec.x.d[1], out->ckeylen);

if (globalinfo.verbose) {
int i;
fprintf(stderr, “Content key:”);
for (i = 0; i < out->ckeylen; i++)
fprintf(stderr, ” %02x”, out->ckey[i]);
fprintf(stderr, “\n”);
}

SHA1(out->ckey, out->ckeylen, out->keyhash);

des_set_key_unchecked((des_cblock *) (&out->keyhash[12]),
out->keysched);

RC4_set_key(&rc4state, 12, out->keyhash);
memset(rc4buff, 0, sizeof(rc4buff));
RC4(&rc4state, sizeof(rc4buff), rc4buff, rc4buff);

memcpy(out->outmask, &rc4buff[48], 8);
memcpy(out->inmask, &rc4buff[56], 8);

MultiSwapSetKey(&out->hashkey, (unsigned int *) rc4buff);
}
/* Stupid little fake XML parser. */

static wchar_t *find_close(wchar_t * str)
{
while ((*str != L’\0′) && (*str != L’>’)) {
if (*str == L'”‘) {
if ((str = wcschr(str + 1, L'”‘)) == NULL)
return NULL;
} str++;
}

if (*str == L’\0′)
return NULL;
else
return str + 1;
}
wchar_t *get_element(wchar_t * tag, wchar_t * str)
{
int len = wcslen(tag);
wchar_t *tmptag;
wchar_t *start, *end;
wchar_t *rval = NULL;

if ((tmptag = malloc((len + 4) * sizeof(wchar_t))) == NULL)
error_exit(“Memory allocation failed in get_element (1)”);

swprintf(tmptag, L”<%s”, tag);

while (1) {
if ((start = wcsstr(str, tmptag)) == NULL)
goto exit;
if (!iswalnum(start[len + 1]))
break;
str = start + len + 1;
}

swprintf(tmptag, L”</%s>”, tag);
end = wcsstr(str, tmptag);

if (end == NULL) {
goto exit;
} else {
wchar_t *realstart = find_close(start);
if ((realstart == NULL) || (realstart > end)) {
goto exit;
} else {
wchar_t *tmp =
malloc((end – realstart +
1) * sizeof(wchar_t));
if (tmp == NULL)
error_exit
(“Memory allocation failed in get_element (2)”);
memcpy(tmp, realstart,
(end – realstart) * sizeof(wchar_t));
tmp[end – realstart] = L’\0′;
rval = tmp;
}
}

exit:
free(tmptag);
return rval;
}

/*
* getDRMDataPath allocates extra room on the end (20 wchars) for
* appending a filename
*/
static wchar_t *getDRMDataPath()
{
HKEY key_drm;
long stat;
DWORD dtype, dlen;
wchar_t *buff;

stat =
RegOpenKeyEx(HKEY_LOCAL_MACHINE, “Software\\Microsoft\\DRM”, 0,
KEY_READ, &key_drm);
if (FAILED(stat))
return NULL;

stat =
RegQueryValueEx(key_drm, “DataPath”, NULL, NULL, NULL, &dlen);
if (FAILED(stat))
return NULL;

if ((buff =
(wchar_t *) malloc(dlen + 20 * sizeof(wchar_t))) == NULL)
error_exit(“Memory allocation failed in getDRMDataPath”);

stat =
RegQueryValueEx(key_drm, “DataPath”, NULL, &dtype,
(uchar *) buff, &dlen);
if (FAILED(stat)) {
free(buff);
return NULL;
}

RegCloseKey(key_drm);

return buff;
}

int fileExistsA(char *fname)
{
return (GetFileAttributes(fname) != -1);
}

int fileExistsW(wchar_t * fname)
{
char buffer[MAX_PATH];
int len;

len = wcslen(fname);
if (WideCharToMultiByte
(CP_ACP, 0, fname, len + 1, buffer, MAX_PATH, NULL, NULL) == 0)
return 0;

return fileExistsA(buffer);
}

void getKSFilename(wchar_t * ksname, char *libname)
{
wchar_t *basepath = getDRMDataPath();
wchar_t currks[MAX_PATH], lastks[MAX_PATH];
char abasepath[MAX_PATH];
char currlib[MAX_PATH], lastlib[MAX_PATH];
int fnum;

if (basepath != NULL) {
WideCharToMultiByte(CP_ACP, 0, basepath,
wcslen(basepath) + 1, abasepath,
MAX_PATH, NULL, NULL);
swprintf(lastks, L”%s\\v2ks.bla”, basepath);
swprintf(currks, L”%s\\v2ksndv.bla”, basepath);
sprintf(lastlib, “BlackBox.dll”);
sprintf(currlib, “%s\\IndivBox.key”, abasepath);
fnum = 1;
while (fileExistsW(currks) && (fileExistsA(currlib))) {
fnum++;
wcscpy(lastks, currks);
swprintf(currks, L”%s\\v2ks%03x.bla”, basepath,
fnum);
strcpy(lastlib, currlib);
sprintf(currlib, “%s\\Indiv%03x.key”, basepath,
fnum);
}
wcscpy(ksname, lastks);
strcpy(libname, lastlib);
free(basepath);
}
}
static int getkeypairs()
{
HMODULE mylib;
int rval;
BBOXOBJ *bbobj;
char errmsg[100];
wchar_t KSFilename[MAX_PATH];
char BBoxLib[MAX_PATH];
int i;

getKSFilename(KSFilename, BBoxLib);
if (globalinfo.verbose) {
fprintf(stderr, “BlackBox library to use: %s\n”, BBoxLib);
fprintf(stderr, “Keystore to use: “);
printwcs(KSFilename);
fprintf(stderr, “\n”);
}
mylib = LoadLibraryA(BBoxLib);
if (mylib == NULL) {
DWORD err = GetLastError();
sprintf(errmsg, “Failed loading library. Err code %08x”,
err);
error_exit(errmsg);
} else {
typedef int (WINAPI * createfn) (BBOXOBJ **,
unsigned short *);
createfn create =
(createfn) GetProcAddress(mylib,
“IBlackBox_CreateInstance2”);
if (create == NULL)
error_exit(“Failed finding proc address.”);
else {
rval = (*create) (&bbobj, KSFilename);

if (bbobj == NULL) {
sprintf(errmsg,
“Failed to create a black box object (err code %08x)\n”,
rval);
error_exit(errmsg);
}

if (globalinfo.verbose) {
fprintf(stderr,
“Created BlackBox instance – extracting key pairs\n”);
}

memcpy(&keypair[0].private, &bbobj->ecprivkey, 20);
memcpy(&keypair[0].public, bbobj->clientid, 40);
numkeypairs = bbobj->numkeypairs + 1;
for (i = 0; i < bbobj->numkeypairs; i++) {
memcpy(&keypair[i + 1].public,
bbobj->keypairs + 60 * i, 40);
memcpy(&keypair[i + 1].private,
bbobj->keypairs + 60 * i + 40, 20);
}

if (globalinfo.verbose) {
fprintf(stderr, “\n”);
for (i = 0; i < numkeypairs; i++) {
fprintf(stderr,
“Public key %d x: “,
i + 1);
printMSBN(&keypair[i].public.x);
fprintf(stderr,
“\nPublic key %d y: “,
i + 1);
printMSBN(&keypair[i].public.y);
fprintf(stderr,
“\nPrivate key %d: “,
i + 1);
printMSBN(&keypair[i].private);
fprintf(stderr, “\n\n”);
}
}
} FreeLibrary(mylib);
} return 0;
}
static CONTKEY *checkLicense(wchar_t * license)
{
wchar_t *ebits = NULL;
wchar_t *pubkey = NULL;
wchar_t *value = NULL;
MS_BN *privkey = NULL;
CONTKEY *ckey = NULL;
MS_BN *thispubkey;
int i;

if ((ebits = get_element(L”ENABLINGBITS”, license)) == NULL)
error_exit(“No ENABLINGBITS element in license!”);

if ((pubkey = get_element(L”PUBKEY”, ebits)) == NULL)
error_exit(“No PUBKEY element in license!”);

if ((value = get_element(L”VALUE”, ebits)) == NULL)
error_exit(“No VALUE element in license!”);

MS_Base64Decode(pubkey, (char **) &thispubkey);
if (globalinfo.verbose) {
fprintf(stderr, “Checking license with PUBKEY “);
printMSBN(thispubkey);
fprintf(stderr, “\n”);
}
for (i = 0; i < numkeypairs; i++) {
if (memcmp(thispubkey, (char *) &keypair[i].public, 40) ==
0) {
privkey = &keypair[i].private;
break;
}
}

if (privkey != NULL) {
if ((ckey = malloc(sizeof(CONTKEY))) == NULL)
error_exit
(“Memory allocation failed in checkLicense”);
if (globalinfo.verbose) {
fprintf(stderr,
“Matched public key! Proceeding…\n”);
} MSDRM_setup(privkey, value, ckey);
}

free(thispubkey);
free(value);
free(pubkey);
free(ebits);

return ckey;
}
static CONTKEY *getContKey(wchar_t * licFile, wchar_t * kid)
{
HRESULT hr;
IStorage *pStg = NULL, *pStgChild = NULL;
IStream *pStrmLicense;
IEnumSTATSTG *penum = NULL;
STATSTG statstg, statstrm;
wchar_t *license = NULL;
unsigned long statLen, reallyRead;
CONTKEY *ckey;

hr = StgOpenStorage(licFile, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0,
&pStg);

if (FAILED(hr))
error_exit(“Couldn’t open license file!”);

hr = pStg->lpVtbl->OpenStorage(pStg, kid, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE,
NULL, 0, &pStgChild);

if (FAILED(hr))
return NULL;

hr = pStgChild->lpVtbl->EnumElements(pStgChild, 0, NULL, 0,
&penum);
if (FAILED(hr))
error_exit(“Couldn’t EnumElements in storage.”);

memset(&statstg, 0, sizeof(statstg));
hr = penum->lpVtbl->Next(penum, 1, &statstg, 0);
while (S_OK == hr) {
hr = pStgChild->lpVtbl->OpenStream(pStgChild,
statstg.pwcsName, NULL,
STGM_READ |
STGM_SHARE_EXCLUSIVE, 0,
&pStrmLicense);

if (FAILED(hr))
error_exit(“Couldn’t open license!”);

hr = pStrmLicense->lpVtbl->Stat(pStrmLicense, &statstrm,
0);

statLen = (unsigned long) statstrm.cbSize.QuadPart / 2;
if ((license =
(wchar_t *) malloc(2 * (statLen + 1))) == NULL)
error_exit
(“Memory allocation failed in getContKey.”);
hr = pStrmLicense->lpVtbl->Read(pStrmLicense, license,
2 * statLen, &reallyRead);

if ((FAILED(hr)) || (reallyRead != 2 * statLen))
error_exit(“License read failed.”);

license[statLen] = 0;

pStrmLicense->lpVtbl->Release(pStrmLicense);

if ((ckey = checkLicense(license + 1)) != NULL) {
free(license);
pStgChild->lpVtbl->Release(pStgChild);
pStg->lpVtbl->Release(pStg);
return ckey;
}

free(license);

hr = penum->lpVtbl->Next(penum, 1, &statstg, 0);
}

return NULL;
}
static void convertKID(wchar_t * kid)
{
while (*kid != L’\0′) {
if (*kid == L’/’)
*kid = L’@’;
else if (*kid == L’!’)
*kid = L’%’;
kid++;
}
}
CONTKEY *MSDRM_init(wchar_t * kid)
{
CONTKEY *ckey;
wchar_t *licfile;

licfile = getDRMDataPath();
if (licfile == NULL)
error_exit(“Couldn’t get DRM data path from registry.”);

wcscat(licfile, L”\\drmv2.lic”);

if (globalinfo.verbose) {
fprintf(stderr, “License file full path: “);
printwcs(licfile);
fprintf(stderr, “\n”);
}

getkeypairs();

convertKID(kid);
ckey = getContKey(licfile, kid);
if (ckey == NULL)
error_exit
(“Couldn’t find a valid license for this content.”);

free(licfile);

return ckey;
}
Viewing message <eed9abd18b5c46b1c1cfc8d2064b057f@noisebox.remailer.org>
From: Anonymous (nobody@noisebox.remailer.org)
Subject: FreeMe README file
Newsgroups: sci.crypt
Date: 2001-10-18 16:26:19 PST
—–BEGIN PGP SIGNED MESSAGE—–
The software distributed with this README file removes content
protection from any Windows Media Audio file (.wma file) that uses DRM
version 2 (as implemented in Windows Media Player version 7). It has
been tested under Windows 98, so may or may not work with other Media
Player/OS combinations. Also be aware that many “protected content”
files out there are actually DRM version 1, especially if they are
older files. This software will not do anything to unprotect version
1 files.

There is another piece of software, called “unfuck”, which similarly
removes protections, but there is a fundamental difference in how
these two pieces of software work. Unfuck works by allowing the
player to unprotect and uncompress the audio, and then captures the
audio samples on the way to the sound card. This software attacks the
problem directly: it simply removes the encryption from the protected
file, leaving everything else exactly the same. Because of this,
there is no loss of quality due to uncompressing and re-compressing
the content — what you’re left with is exactly the original content,
just not protected. It’s also much faster.

Please be aware that this software is “proof-of-concept” or
“demonstration” level, not production level. While every effort has
been made to make sure that it works properly, it may fail in
unforeseen situations — it has NOT been thoroughly tested! There
isn’t much chance that this program will screw up your system, but use
at your own risk!

WARNING!!!!! I have just learned that the new Microsoft Media
Player EULA includes a clause that says they can *automatically*
modify the software on your system, without any confirmation from
you required! In other words, they can disable your software, or
force an upgrade so that FreeMe won’t work, just because they feel
like it. Be careful out there!
CONTENTS:
README – This file
LICENSE – Yes, a license! Read it!
Technical – Full technical description of how MS DRMv2 works
Philosophy – My philosophy on why I released this, and what’s wrong
with the DMCA
FreeMe.exe – The actual program
src/ – The source code

The first 4 files can possibly be widely re-distributed and mirrored
without much fear of real legal worries — however, you will almost
certainly be harassed by several big and powerful companies, so keep
that in mind. The last two files (the program) cause more problems:
distributing these in the U.S. is almost certainly a violation of
the DMCA. However, outside the U.S. should be mostly ok — so
mirror these on as many foreign sites as possible! Again, you may
be harassed by big and powerful companies, and might get threatening
letters from lawyers, so be prepared for that.
INSTALLATION:
There’s just a single executable file “FreeMe.exe” to install. You
can copy it so that it’s in your executable PATH (for example, copy
to directory C:\WINDOWS\COMMAND), or you can put a shortcut to it on
your desktop — see below.
USAGE:
This is a command-line program, and the best way to run it is from
the command line. If it is installed, and the executable is in your
PATH, all you have to do is type “freeme x.wma” at a DOS prompt in
order to unprotect the file “x.wma”. There is a verbose flag that
you can invoke to have it print out all sorts of information as it
discovers it (your public/private key pairs, KID of the file you’re
unprotecting, content key, etc.). For example, typing “freeme -v
x.wma” unprotects the file as above, but in verbose mode. The
output file will be called “Freed-x.wma”, where “x.wma” is the
original filename.

One problem with this being a command line utility is that many
audio files have very long file names, so you’ll have to put the
filename in quotes in order for this to work, like so:
Prompt> freeme “Billy and the Boingers – The RIAA Stole My Soul.wma”

As an alternative, you can put a shortcut to the FreeMe.exe
executable on your desktop, and then can simply drag files from the
file explorer to FreeMe. However, there is one big problem with
this: the filename given to FreeMe is actually the short filename,
so if you did this with the file above, you’d end up with an output
file named something like “Freed-BILLYA~1.WMA” Unfortunately, I
don’t know how to fix this — maybe someone else out there does.
SOURCE CODE:
The full source code is included in the src/ directory, but you will
need a Win32 version of the OpenSSL package (and crypto library) in
order to compile it.

There are some definite problems with this code, which I would
suggest to people interested in improving what I’ve distributed:
First off, a lot of things in Windows seem to be designed to be
easier in C++ than in C; unfortunately, I don’t know a lot about
Windows programming, and never have learned or used C++ at all, so
some of my code may be a little strange in its approach. Secondly,
my .asf/.wma file format processing is hopelessly naive. Surely
there are better ways to do this, or existing libraries to do it.
Also, I don’t really do XML parsing, but just a very simplistic
scanning. This seems to work for every license I’ve seen, but using
a real XML parser would make this much more robust. Finally, a nice
pretty GUI would be good, but wasn’t necessary for my
“proof-of-concept” code, so I didn’t include it.

Finally, you know that Microsoft is going to make some changes that
will render my software useless. You’ve got the source code, so use
that as a starting point to change with them.
HOW TO CONTACT ME:
Being anonymous, it’s hard to give a way to contact me. However, if
you have something very important to tell me, post to the sci.crypt
Usenet newsgroup with a subject that includes the phrase “To Beale
Screamer”. My PGP key is given in this distribution, in case you
need it (and I will always sign anything I distribute).

Please don’t inundate the poor people in sci.crypt with a bunch of
pointless comments. But I did want to give people some avenue for
contacting me if absolutely necessary.
MESSAGES:
I have included messages below for specific groups of people.

Users: Please respect the uses I have intended this software for. I
want to make a point with this software, and if you use it for
purposes of violating copyrights, the message stands a very good
chance of getting lost. Also, Microsoft is obviously going to
release patches to their media player in order to get around the
exploit in my software — I think you’ll be safe it you refuse
to upgrade from your current version of the Windows Media Player
(but see the warning above about “forced upgrades”). Unless they
want to sacrifice backward compatibility, you will at least be
able to work with your current (legally obtained) media files
for the near future.

Microsoft: You guys have put together a pretty good piece of
software. Really. The only real technical flaw is that
licenses can’t be examined for their restrictions once they are
obtained. My real beef is with the media publishers’ use of this
software, not the technology itself. However, it’s easy to see
where software bloat and inefficiency comes from when this code
is examined: every main DLL has a separate copy of the elliptic
curve and other basic crypto routines, and parameters passed
back and forth between modules are encrypted giving unnecessary
overhead, not to mention all the checks of the code integrity,
checks for a debugger running, code encryption and decryption.
Perhaps you felt this was necessary for the “security through
obscurity” aspect, but I’ve got to tell you that this really
doesn’t make a bit of difference. Make lean and mean code,
because the obscurity doesn’t work as well as you think it does.
Also read the message below to the Justice Department!

Justice Department: Maybe this should really be addressed to the
state officials, since it looks like the current U.S.
administration doesn’t care too much about monopoly powers being
abused. But for whoever is interested, there is a very serious
anti-competitive measure in this software. In particular, for
various modules of the software to be used, you must supply a
certified public key for communication. Guess who controls the
certification of public keys? Microsoft. So if someone wants
to make a competing product, which integrates well with the
Windows OS, you will need to get Microsoft’s permission and
obtain a certificate from them. I don’t know what their policy
is on this, so don’t know if this power will be abused or not.
However, it has the potential for being a weapon Microsoft can
use to knock out any competition to their products.

Artists: Don’t fear new distribution methods — embrace them.
Technology is providing you the means to get your art directly
to consumers, avoiding the big record companies. They want a
piece of the action for YOUR creativity, and you don’t need to
let them in on it any more. Your fans will treat you nicely,
unless you treat your fans poorly (take note of that Lars). Bo
Diddley didn’t have anything to fear from his fans, but a lot to
fear from Leonard Chess. Think about that.

Publishers: Give us more options, not fewer. If you try to take
away our current rights, and dictate to us what we may or may
not do, you’re going to get a lot of resistance. You better
find a way to play nicely soon, because technology is making it
possible for artists to make do without you at all. Try getting
some progressive thinkers into management — current people
don’t seem to be able to cope with the new environment that is
emerging.

– – —

Original Distribution Date: October 18, 2001
by “Beale Screamer”
—–BEGIN PGP SIGNATURE—–
Version: 2.6.2

iQCVAwUBO84IDZCr1f2GXCalAQGRcAP8CHkU6B42NZNiuhS/roxKJTljm36Doq+R
zrqFeO2JY9xMCMhBlYP6RRkDATdlMWNj/U3DLXJ/lBJYUeSwMT3vsUTUHOA/lGMQ
9VqYHmAEwImnKWBNDG694abVeCFa9H/FziLLjeJQ73ADcfjr4rJ/FpHMxrtb2YfF
K5QaP3QRXl0=
=RK34
—–END PGP SIGNATURE—–

Viewing message <QDS2GL5737182.7715972222@anonymous.poster>
From: Anonymous (anonymous@anonymous.poster.com)
Subject: FreeMe source – main.c
Newsgroups: sci.crypt
Date: 2001-10-18 16:31:25 PST
Split into .c and .h files

——————— Start of main.c ———————-

/*
* FreeMe main.c — mostly a wma/asf file processor, with DRM part
* put off into msdrm.c
*/

#include <windows.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include “msdrm.h”

typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;

#pragma pack(1)
typedef struct chunk_save_st {
GUID guid;
uint64_t len;
void *data;
struct chunk_save_st *next;
} CHUNKSAVE;

typedef int (*GUIDHANDLER) (FILE * fp, CHUNKSAVE * savep);

typedef struct guidaction_st {
GUID *guid;
char *name;
GUIDHANDLER fn;
} GUIDACTION;

typedef struct fileheader_st {
GUID clientGUID;
uint64_t filesize;
uint64_t fileCreateTime;
uint64_t numPackets;
uint64_t timeAtEnd;
uint64_t playDuration;
uint32_t timeAtStart;
uint32_t unknown1;
uint32_t unknown2;
uint32_t packetSize;
uint32_t packetSize2;
uint32_t uncompressedSize;
} FILEHEADER;

int handle_chunk(FILE * fp, CHUNKSAVE * chunk);
int handle_header(FILE * fp, CHUNKSAVE * chunk);
int handle_file_header(FILE * fp, CHUNKSAVE * chunk);
int handle_data(FILE * fp, CHUNKSAVE * chunk);
int handle_stream_header(FILE * fp, CHUNKSAVE * chunk);
int handle_copy(FILE * fp, CHUNKSAVE * chunk);
int handle_drmv1(FILE * fp, CHUNKSAVE * chunk);
int handle_drmv2(FILE * fp, CHUNKSAVE * chunk);

struct globalinfo_st globalinfo;

GUID HeaderGUID = { 0x75b22630, 0x668e, 0x11cf, {0xa6, 0xd9,
0x00, 0xaa, 0x00, 0x62,
0xce, 0x6c}
};

GUID DataGUID = { 0x75b22636, 0x668e, 0x11cf, {0xa6, 0xd9,
0x00, 0xaa, 0x00, 0x62,
0xce, 0x6c}
};

GUID FileHeaderGUID = { 0x8cabdca1, 0xa947, 0x11cf, {0x8e, 0xe4,
0x00, 0xc0, 0x0c,
0x20, 0x53, 0x65}
};

GUID StreamHeaderGUID = { 0xb7dc0791, 0xa9b7, 0x11cf, {0x8e, 0xe6,
0x00, 0xc0, 0x0c,
0x20, 0x53, 0x65}
};

GUID AudioStreamGUID = { 0xf8699e40, 0x5b4d, 0x11cf, {0xa8, 0xfd,
0x00, 0x80, 0x5f,
0x5c, 0x44, 0x2b}
};

GUID Unknown1GUID = { 0x5fbf03b5, 0xa92e, 0x11cf, {0x8e, 0xe3,
0x00, 0xc0, 0x0c, 0x20,
0x53, 0x65}
};

GUID Unknown2GUID = { 0x86d15240, 0x311d, 0x11d0, {0xa3, 0xa4,
0x00, 0xa0, 0xc9, 0x03,
0x48, 0xf6}
};

GUID DRMv2HeaderGUID = { 0x298ae614, 0x2622, 0x4c17, {0xb9, 0x35,
0xda, 0xe0, 0x7e,
0xe9, 0x28, 0x9c}
};

GUID DRMv1HeaderGUID = { 0x2211b3fb, 0xbd23, 0x11d2, {0xb4, 0xb7,
0x00, 0xa0, 0xc9,
0x55, 0xfc, 0x6e}
};

GUID ContentDescrGUID = { 0x75b22633, 0x668e, 0x11cf, {0xa6, 0xd9,
0x00, 0xaa, 0x00,
0x62, 0xce, 0x6c}
};

GUID PropertyListGUID = { 0xd2d0a440, 0xe307, 0x11d2, {0x97, 0xf0,
0x00, 0xa0, 0xc9,
0x5e, 0xa8, 0x50}
};

GUIDACTION known_guids[] = {
{&HeaderGUID, “Header”, handle_header},
{&DataGUID, “Data”, handle_data},
{&FileHeaderGUID, “File Header”, handle_file_header},
{&StreamHeaderGUID, “Stream Header”, handle_copy},
{&Unknown1GUID, “Header subchunk – unknown 1”, handle_copy},
{&Unknown2GUID, “Header subchunk – unknown 2”, handle_copy},
{&DRMv2HeaderGUID, “DRMV2 ContentHeader”, handle_drmv2},
{&DRMv1HeaderGUID, “DRMv1 header”, handle_drmv1},
{&ContentDescrGUID, “Content Description”, handle_copy},
{&PropertyListGUID, “Property List”, handle_copy},
{NULL, NULL, NULL}
};
void error_exit(char *msg)
{
fprintf(stderr, “%s\n”, msg);
fprintf(stderr, “\n Press <ENTER> to acknowledge error.\n”);
getchar();
exit(1);
}

void printwcs(wchar_t * msg)
{
int len, rval;
char *buff;

len = wcslen(msg);
if ((buff = malloc(len + 1)) == NULL)
error_exit(“Memory allocation failed in printwcs”);

rval = WideCharToMultiByte(CP_ACP, 0, msg, len + 1, buff, len + 1,
NULL, NULL);
if (rval == 0)
error_exit(“WideCharToMultiByte failed in printwcs”);

fputs(buff, stderr);
free(buff);
}
GUIDHANDLER find_guid(GUID * guid)
{
GUIDHANDLER handler = NULL;
GUIDACTION *curr;

curr = known_guids;
while (curr->guid != NULL) {
if (memcmp(curr->guid, guid, sizeof(GUID)) == 0) {
handler = curr->fn;
break;
} curr++;
}

return handler;
}
int handle_copy(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long datalen = (unsigned long) (chunk->len – 24);

chunk->data = malloc(datalen);

if (chunk->data == NULL)
error_exit(“Memory allocation failed in handle_copy”);

if (fread(chunk->data, datalen, 1, fp) != 1) {
free(chunk->data);
chunk->data = NULL;
return 0;
}

return 1;
}
int handle_drmv1(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long toskip = (unsigned long) (chunk->len – 24);

fseek(fp, toskip, SEEK_CUR);
globalinfo.hasV1header = 1;
if (globalinfo.verbose)
fprintf(stderr, “Found DRMv1 header object.\n”);
return 1;
}
int handle_drmv2(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long datalen = (unsigned long) (chunk->len – 24);
unsigned short *data = malloc(datalen);

if (globalinfo.verbose)
fprintf(stderr, “Found DRMv2 header object.\n”);

if (data == NULL)
error_exit(“Memory allocation in handle_drmv2 failed.”);

fseek(fp, 6, SEEK_CUR);
if (fread(data, datalen – 6, 1, fp) != 1)
error_exit(“Data read in handle_drmv2 failed.”);

data[(datalen – 6) / 2] = L’\0′;

globalinfo.kid = get_element(L”KID”, data);
globalinfo.hasV2header = 1;

if (globalinfo.verbose) {
if (globalinfo.kid == NULL) {
fprintf(stderr,
“KID not found in header object!\n”);
} else {
fprintf(stderr, “Found KID (“);
printwcs(globalinfo.kid);
fprintf(stderr, “)\n”);
}
}

free(data);
return 1;
}
int handle_packet(FILE * fp, int packetlen)
{
struct packethead_st {
uchar id;
short unknown1;
uchar flags;
uchar segTypeID;
} *info;
uchar *data;
int flagoffset = 13;
int objlen;
int dataoffset;
int rval = 0;

if ((data = malloc(packetlen)) == NULL)
error_exit(“Memory allocation failed in handle_packet.”);

if (fread(data, packetlen, 1, fp) != 1)
goto exit;

info = (struct packethead_st *) data;
if (info->id != 0x82)
error_exit(“Unknown packet id – don’t know what to do!”);

if (info->flags & 0x40)
flagoffset += 2;

if (info->flags & 0x10)
flagoffset += 2;
else if (info->flags & 0x08)
flagoffset += 1;

if (info->flags & 0x01)
flagoffset += 1;

if (info->segTypeID == 0x55)
flagoffset += 1;
else if (info->segTypeID == 0x59)
flagoffset += 2;
else if (info->segTypeID == 0x5d)
flagoffset += 4;

if (data[flagoffset] != 8)
error_exit
(“Flag says grouping – don’t know how to do this!”);

dataoffset = flagoffset + 9;
if (info->flags & 0x01)
error_exit(“Need the data_length field – don’t know how!”);

objlen = *((int *) &data[flagoffset + 1]);

MSDRM_decr_packet(data + dataoffset, objlen,
globalinfo.content_key);

fwrite(data, packetlen, 1, stdout);
rval = 1;

exit:
free(data);
return rval;
}
int handle_data(FILE * fp, CHUNKSAVE * chunk)
{
struct datahead_st {
GUID unknownGUID;
uint64_t numPackets;
uchar unknown[2];
} datahead;
int packetcount = 0;
int lastpercent = -1;

if (fread(&datahead, sizeof(datahead), 1, fp) != 1)
return 0;

fwrite(chunk, 24, 1, stdout);
fwrite(&datahead, sizeof(datahead), 1, stdout);

if (globalinfo.verbose) {
fprintf(stderr, “Starting to process data packets\n”);
fprintf(stderr, “%d packets of length %d\n”,
globalinfo.numpackets, globalinfo.packetlen);
}

while (handle_packet(fp, globalinfo.packetlen)) {
packetcount++;
if (globalinfo.numpackets != 0) {
int percent, i;
percent =
((packetcount * 200) / globalinfo.numpackets +
1) / 2;
if (percent != lastpercent) {
fprintf(stderr, “|”);
for (i = 0; i < percent / 2; i++)
fprintf(stderr, “#”);
for (; i < 50; i++)
fprintf(stderr, ” “);
fprintf(stderr, “| “);
fprintf(stderr, “%3d%%\r”, percent);
lastpercent = percent;
}
}
}
fprintf(stderr, “\n”);

return 0;
}
int handle_header(FILE * fp, CHUNKSAVE * chunk)
{
struct header_st {
int numchunks;
short unknown;
} header;
int i;
CHUNKSAVE *subchunk = NULL;
int savecount = 0;
CHUNKSAVE *head = NULL, *tail = NULL;
FILEHEADER *fileheader;
uint64_t bytesremoved = 0;

if (fread(&header, sizeof(header), 1, fp) != 1)
return 0;

for (i = 0; i < header.numchunks; i++) {
if (subchunk == NULL) {
if ((subchunk = malloc(sizeof(CHUNKSAVE))) == NULL)
error_exit
(“Memory allocation failed in handle_header”);
}
if (!handle_chunk(fp, subchunk))
return 0;
if (subchunk->data != NULL) {
if (tail == NULL)
head = subchunk;
else
tail->next = subchunk;
subchunk->next = NULL;
tail = subchunk;

if ((subchunk = malloc(sizeof(CHUNKSAVE))) == NULL)
error_exit
(“Memory allocation failed in handle_header”);

savecount++;
} else { bytesremoved += subchunk->len;
}
}

if (globalinfo.fileheader == NULL) {
error_exit(“Didn’t see file header!”);
} else {
CHUNKSAVE *currchunk, *nextchunk;

if (!globalinfo.hasV2header) {
if (globalinfo.hasV1header)
error_exit
(“This file is version 1 protected, not version 2.”);
else
error_exit
(“This file doesn’t seem to be protected!”);
}

if (!globalinfo.kid)
error_exit
(“Version 2 protected, but no KID found!”);

if (globalinfo.verbose)
fprintf(stderr, “Starting to look for license.\n”);

globalinfo.content_key = MSDRM_init(globalinfo.kid);
if (globalinfo.content_key == NULL)
error_exit(“Couldn’t find a valid license!”);

if (freopen(globalinfo.ofname, “wb”, stdout) == NULL)
error_exit(“Couldn’t open output file.”);

if (globalinfo.verbose)
fprintf(stderr, “Opened output file <%s>\n”,
globalinfo.ofname);

currchunk = head;
fileheader =
(FILEHEADER *) ((CHUNKSAVE *) globalinfo.fileheader)->
data;
fileheader->filesize -= bytesremoved;
globalinfo.packetlen = fileheader->packetSize;
globalinfo.numpackets = (int) fileheader->numPackets;
header.numchunks = savecount;
chunk->len -= bytesremoved;
fwrite(chunk, 24, 1, stdout);
fwrite(&header, sizeof(header), 1, stdout);
while (currchunk != NULL) {
fwrite(currchunk, 24, 1, stdout);
fwrite(currchunk->data,
(unsigned long) (currchunk->len – 24), 1,
stdout);
nextchunk = currchunk->next;
free(currchunk->data);
free(currchunk);
currchunk = nextchunk;
}
}

return 1;
}
int handle_file_header(FILE * fp, CHUNKSAVE * chunk)
{
unsigned long datalen = (unsigned long) (chunk->len – 24);

if ((chunk->data = malloc(datalen)) == NULL)
error_exit
(“Data allocation failed in handle_file_header.”);

if (fread(chunk->data, datalen, 1, fp) != 1) {
free(chunk->data);
chunk->data = NULL;
return 0;
}

globalinfo.fileheader = chunk;

return 1;
}
int handle_chunk(FILE * fp, CHUNKSAVE * chunk)
{
GUIDHANDLER handler = NULL;
int retval = 0;

chunk->data = NULL;

if (fread(&chunk->guid, sizeof(chunk->guid), 1, fp) != 1)
return 0;

if (fread(&chunk->len, sizeof(chunk->len), 1, fp) != 1)
return 0;

handler = find_guid(&chunk->guid);

if (handler != NULL) {
retval = (handler) (fp, chunk);
} else {
long toskip = (long) (chunk->len – 24);
retval = (fseek(fp, toskip, SEEK_CUR) == 0);
retval = 1;
}

return retval;
}
int main(int argc, char *argv[])
{
CHUNKSAVE chunk;
int more = 1;
FILE *ifp;
char *fnamestart;
static char ofname[1000];

globalinfo.verbose = 0;
globalinfo.fileheader = NULL;
globalinfo.kid = NULL;
globalinfo.hasV1header = 0;
globalinfo.hasV2header = 0;
globalinfo.ofname = ofname;

if ((argc < 2) || (argc > 3))
error_exit(“Usage: FreeMe [-v] protectedfile”);

if ((strcmp(argv[1], “-v”) != 0) && (argc == 3))
error_exit(“Usage: FreeMe [-v] protectedfile”);

if (argc == 3)
globalinfo.verbose = 1;

if ((ifp = fopen(argv[argc – 1], “rb”)) == NULL) {
sprintf(ofname, “Couldn’t open input file (%s)”,
argv[argc – 1]);
error_exit(ofname);
}

ofname[0] = ‘\0’;
if ((fnamestart = strrchr(argv[argc – 1], ‘\\’)) != NULL) {
memcpy(ofname, argv[argc – 1],
fnamestart – argv[argc – 1] + 1);
ofname[fnamestart – argv[argc – 1] + 1] = ‘\0’;
}
strcat(ofname, “Freed-“);
strcat(ofname, (fnamestart ? fnamestart + 1 : argv[argc – 1]));

while (more)
more = handle_chunk(ifp, &chunk);

return 0;
}

______________________________________________________________________________
Posted Via Binaries.net = SPEED+RETENTION+COMPLETION = http://www.binaries.net