Skip to content

Latest commit

 

History

History
447 lines (326 loc) · 30 KB

README.md

File metadata and controls

447 lines (326 loc) · 30 KB

A cryptographic solution to Cellebrite EAS ciphertext

Author: Matt Bergin (@thatguylevel)

Introduction

This paper details a cryptographic solution to the .eas file encryption used by Cellebrite to prevent review of the forensic methodologies implemented in their software.

This is the second cipher written by Cellebrite I have decided to release a solution to. The previous research I published detailed how to recover the plaintext for code used by their tools to elevate privilege on target devices and to collect raw bytes needed for forensic analysis. These findings were presented at BlackHat 2021 Asia and much of my code can be found on my public GitHub. In both my previous work and now the algorithms discussed are affected by CWE-321, CWE-326, and CWE-327.

I would also like to show an appreciation for the book Cryptanalysis: A study of ciphers and their solutions by Helen-Fouche Gaines. It was valuable to this research and I came across it randomly while visiting a local library, go visit yours and walk around for a bit.

Learning from the past

In my previous research, I found out too late about solutions for previous versions that had already been made available by others. When starting to look at this cipher I knew of a previous solution that was made public by cellebrited. The source code for their solution can be found below:

import os
import sys
import random

class CellebriteEas(object):
    def __init__(self):
        self._box = bytearray(256)
        self.KEY_SIZE = 16 #Decodieren
        self.BOX_SIZE = 256
        self._encryptionKey=None
        
    def setKey(self,Key):
        self._encryptionKey=Key
        for index in range(256):
            self._box[index] = index;
        index1 = 0
        keylength = len(self._encryptionKey)
        for index2 in range(256):
            index1 = (index1 +  self._box[index2] + self._encryptionKey[index2 % keylength]) % 256
            num = self._box[index2]
            self._box[index2] = self._box[index1]
            self._box[index1] = num

    def crypto(self,data):
        index1 = 0
        index2 = 0
        numArray1 = bytearray(len(data))
        numArray2 = bytearray()
        numArray2+=self._box
        for index3 in range(len(data)):
            index1 = (index1 + 1) % 256
            index2 = (index2 + numArray2[index1]) % 256
            num1 = numArray2[index1]
            numArray2[index1] = numArray2[index2]
            numArray2[index2] = num1
            num2 = data[index3]
            num3 = numArray2[( numArray2[index1] + numArray2[index2]) % 256]
            numArray1[index3] = ( num2 ^ num3)&0xff
        return numArray1    
            
    def decrypt(self,filename):
        buffer=bytearray()
        with open(filename,"rb") as file:
            file_data=file.read()
            Key=bytearray(file_data[-16:])
            Data=bytearray(file_data[:-16])
            self.setKey(Key)
            return self.crypto(Data)
        
def decrypt_file(filename):
    enc = CellebriteEas()
    with open(filename+".dll","wb") as file:
        file.write(enc.decrypt(filename))

def do_decrypt(basedir,hash):
    print("Im {0} running".format(basedir))
    for root, dirs, files in os.walk(basedir):
        for name in files:
            if ".eas"==name[-4:]:
                print("{0} processing".format(name))
        filename=os.path.join(root,name)
        decrypt_file(filename)

decrypt_file("bb_whatsapp.eas")

Evaluating the present

In this version of the .eas file encryption algorithm, the key to unlock the file exists within the file. The first byte is not only an encrypted byte, but also acts as part of an offset to the location within the ciphertext where the key resides. I don't think this is a subtle attempt at a concealment cipher but it certainly felt a bit like a variation of a grille cipher. Looking at an example we see this:

$ hexdump -C Apple.eas | more
00000000  c3 5c 44 89 c4 4f 3a 60  59 66 af cd d3 2d 33 f3  |.\D..O:`Yf...-3.|
00000010  37 fd 71 ed a7 8f 23 04  12 28 13 b5 2a f6 84 5b  |7.q...#..(..*..[|
...
000000c0  31 f0 40 81 14 c1 70 01  08 1e d9 f9 f6 ed c6 5c  |1.@...p........\|
000000d0  c6 13 70 0f 54 6f 78 70  6e 6b 78 07 0f 59 71 61  |..p.Toxpnkx..Yqa|
000000e0  59 3f fe 4c df 7c e9 55  39 76 55 e2 14 08 78 01  |Y?.L.|.U9vU...x.|
...

The offset to the key is calculated using the following formula:

Normal

The output of the formula shown above is 0xd2 and this is the location of the key material needed to unlock the file. Here is the key from this example:

000000d0  XX XX 70 0f 54 6f 78 70  6e 6b 78 07 0f 59 71 61  |..p.Toxpnkx..Yqa|
000000e0  59 3f XX XX XX XX XX XX  XX XX XX XX XX XX XX XX  |Y?..............|

This sixteen byte key is removed from the ciphertext to be decrypted and is used to initialize two substitution boxes. To begin, a 256 byte 2D matrix is generated and populated with 0x0 through 0xff. Below is a matrix showing what this looks like:

Normal

Next we calculate the new value for the first of two indices. A for loop is then used to perform the necessary transpositions in order to finish the initialization of this substitution box. This is done using the following forumla, where index1 is initially set to 0 and i is a range from 0 through 256.

Normal

The second index is the value within the box at the position of the value for i.

Normal

The value within the box at the position of i is replaced with the value within the box at the position index1.

Normal

The value within the box at the position of index1 is replaced with the value within the box at the position of index2.

Normal

Below is an example showing the log output of the transposition for the first substitution box:

TRANSPOSE SBOX1[0] was 0x0 now 0x70. SBOX1[112] was 0x70 now 0x0
TRANSPOSE SBOX1[1] was 0x1 now 0x80. SBOX1[128] was 0x80 now 0x1
TRANSPOSE SBOX1[2] was 0x2 now 0xd6. SBOX1[214] was 0xd6 now 0x2
TRANSPOSE SBOX1[3] was 0x3 now 0x48. SBOX1[72] was 0x48 now 0x3

Below is an example of the first substition box after it has been initialized:

Normal

The second byte of the sixteen byte key is then used to initialize the second substitution box. This byte is first made the only byte in a 624 byte 2D matrix. The remainder of the matrix is populated in a for loop using the following formula where i is the value of the last byte in the matrix:

Normal

The next value is then appended to the matrix. The value for i in the subsequent operation then becomes 0x3 and this formula is repeated. The first 256 bytes of the second substitution box in this case is:

Normal

Two copies of substitution box two should be kept in memory. One which is transposed earlier in the decryption process and another which is transposed mid-way through the overall decryption process.

Recovering the first byte

Prior to attempting to decrypt an encrypted byte, the two substitution boxes go through a set of transposition operations which mutate the boxes based on the given formulas and box content. It is important to note that these transpositions are applied for every byte to be decrypted. The first substitution box undergoes one transpostition while the second undergoes three. Before these transpositions take place a check is performed to see if we are decrypting byte number 0x26f, and if so, then to restore the original content of the second substitution box before beginning the transposition and decryption operation within the loop.

The transposition operation for the first substitution box is very similar to what is used during its initialization. The main difference is that in this case we are not mixing in bytes from a key when calculating new values and positions. Before we enter the for loop for the first time, we set two variables, index1 and index2, to zero. We calculate the first position (index1) using the following:

Normal

We need to also store the old index1 value for later use.

Normal

Next we calculate the second position (index2) using the following:

Normal

The values at these positions are then swapped.

Normal

Normal

No transposition is performed on the second substitution box for bytes before 0x26e. Instead, we then select the bytes needed from each of the two boxes and also the previous value at index1 from substitution box 1. When decrypting the first byte, the previous value is also the current value.

Normal

Normal

Normal

To decrypt an encrypted byte we plug in our variables to the following (simplified) equation:

Normal

Going back to our example from Apple.eas, the first encrypted byte is 0xc3. We expect to get back 0x4d (ASCII: M) as it is the first character in the file magic for the Portable Executable (PE) file format. In our equation below, we do not get that result. I chose Apple.eas on purpose because it is one of a handful of encrypted files that were stubborn.

(((((0xf << 0xb & 0xff) >> 0x7) >> 0x12 ^ 0xf) ^ 0xca) ^ 0xc3) ^ 0xcb = 0xcd

The above equation works for most files. In the case where it fails (which you should check using the same technique described above) a simple fix can be applied to substitution box 2 that will address the issue and allow for the file to be decrypted. The fix is to re-initialize the second substitution box using a different value. We start by setting i to the value of the second byte of the encryption key and then add 0x80.

Normal

Normal

The first 256 bytes of substitution box 2 in this case are:

Normal

Now, in applying the same equation as before we can successfully decrypt the first byte.

(((((0x8f << 0xb & 0xff) >> 0x7) >> 0x12 ^ 0x8f) ^ 0xca) ^ 0xc3) ^ 0xcb = 0x4d

This approach will work for the first 0x26f bytes by design, but will also work through 0x273 due to insufficient transposition in substitution box 2 during each loop.

Combining transposition and substitution

After byte 0x26f, three additional transposition operations take place when decrypting, totaling four transpositions per byte.

Twist 1

In the first twist we iterate over substitution box two 0xe3 times. At the start of each iteration, we select byte i+1 and right shift it by 1 then and it by 1.

Normal

Normal

Normal

From here if b2 is 0 then c will be 0, otherwise c is 0xdf.

Normal

Normal

Normal

Normal

Twist 2

In the second twist we iterate over substitution box two 0x18c times. At the start of each iteration, we select byte i+0xe3 and right shift it by 1 then and it by 1.

Normal

Normal

Normal

From here if b2 is 0 then c will be 0, otherwise c is 0xdf.

Normal

Normal

Normal

Normal

Twist 3

In the third twist there is no loop.

Normal

Normal

Normal

From here if b2 is 0 then c will be 0, otherwise c is 0xdf.

Normal

Normal

Normal

Normal

Recovering the remaining bytes

Now that all of the substitution box transpositions have been completed for the next byte, we can select values from the boxes and plug them into an equation which will return a plaintext byte.

Normal

Normal

c = encrypted[pos]

Finally..

Normal

Demo

$ python3 ./decrypt-eas --file Apple.eas --verbose
[+] Decrypting: Apple.eas
[*] OFFSET 0xd2
[*] IV 0x700f546f78706e6b78070f597161593f

SBOX1 0x100
0x70 0x07 0x59 0x86 0xc4 0x39 0x20 0x5f 0x9f 0x69 0xfd 0x2c 0xa9 0x00 0xd9 0xcc
0x56 0x19 0xd2 0x49 0x4f 0x43 0xe9 0xcd 0xf1 0xd5 0x3a 0x62 0x64 0xb9 0x30 0x76
0xad 0x2f 0xb1 0xf5 0x6a 0x08 0x48 0x58 0xb2 0xd1 0x5c 0x44 0x2d 0x3d 0xd8 0x36
0x09 0xef 0x75 0x37 0x05 0x5b 0x1a 0x21 0x3c 0xa3 0xa7 0x4e 0x8f 0xaa 0xce 0x4d
0x63 0xe7 0x8e 0x25 0xac 0xd3 0x47 0x32 0x34 0x8d 0x11 0x4c 0xcf 0x66 0x2e 0x74
0x88 0x96 0x71 0x97 0x87 0xa4 0x8a 0x12 0xa2 0xf9 0xe1 0x67 0x1d 0x77 0xb4 0x0f
0x6d 0xfe 0xbf 0x98 0xbe 0xb5 0x89 0x93 0xe6 0xab 0x45 0x40 0x92 0x9c 0xae 0x9d
0x17 0x06 0x53 0x28 0x78 0x0d 0xbc 0x84 0xa5 0x0b 0x7d 0xa1 0x0a 0x6c 0x01 0x7c
0x80 0x55 0xc6 0xe0 0x23 0x73 0x0e 0xf6 0x13 0x42 0x29 0x52 0xdd 0xd0 0xb7 0x5e
0x38 0xd4 0xeb 0xed 0xb6 0x10 0x7b 0x5a 0xd7 0x26 0x22 0xf0 0xea 0x65 0xf4 0xc0
0xcb 0xe8 0x41 0x03 0xf2 0x83 0xdc 0x8b 0x6b 0x16 0xc1 0xff 0x72 0x60 0xee 0xbd
0x04 0x82 0xa6 0xc5 0x18 0xdf 0x54 0x1e 0xc7 0x9e 0xf3 0x1f 0x57 0x27 0x79 0xb0
0xc2 0x2a 0x24 0x50 0x6e 0x1c 0x2b 0xfa 0x0c 0xfb 0x6f 0x7a 0x5d 0xba 0x7f 0xc9
0x51 0x15 0x99 0x14 0x95 0x02 0x9b 0x85 0x90 0x7e 0x4a 0xd6 0x35 0xe4 0xfc 0x4b
0xda 0xa8 0x1b 0x8c 0xe2 0x61 0x31 0x3f 0x46 0xf7 0xe3 0xca 0xde 0xec 0xe5 0x68
0xa0 0xb3 0x91 0x33 0x3b 0x3e 0xc3 0xbb 0xdb 0xf8 0x94 0x81 0xc8 0x9a 0xb8 0xaf

SBOX2 0x270
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b
0x4f 0x43 0xa7 0xbb 0xbf 0xf3 0x97 0xeb 0x2f 0xa3 0x87 0x1b 0x9f 0x53 0x77 0x4b
0x0f 0x03 0x67 0x7b 0x7f 0xb3 0x57 0xab 0xef 0x63 0x47 0xdb 0x5f 0x13 0x37 0x0b
0xcf 0xc3 0x27 0x3b 0x3f 0x73 0x17 0x6b 0xaf 0x23 0x07 0x9b 0x1f 0xd3 0xf7 0xcb
0x8f 0x83 0xe7 0xfb 0xff 0x33 0xd7 0x2b 0x6f 0xe3 0xc7 0x5b 0xdf 0x93 0xb7 0x8b

(((((0xf << 0xb & 0xff) >> 0x7) >> 0x12 ^ 0xf) ^ 0xca) ^ 0xc3) ^ 0xcb == 0xcd
[!] The first byte has failed to decrypt. Do you want to try a experimental fix? (y/n) y
[-] Fix applied, trying again.
(((((0x8f << 0xb & 0xff) >> 0x7) >> 0x12 ^ 0x8f) ^ 0xca) ^ 0xc3) ^ 0xcb == 0x4d
Do you want to keep going? (y/n/q/s/t) t

...
...
...

SBOX1 0x100
0x9a 0x8c 0x7c 0xaf 0x57 0x69 0x2a 0x31 0xbf 0x43 0x27 0x4a 0xf4 0x36 0x09 0x72
0x15 0xc7 0xe8 0x33 0xe7 0x8d 0xf7 0x34 0x03 0x54 0x10 0x14 0x4f 0xd4 0x46 0x3b
0xaa 0x52 0xad 0xcf 0x35 0x9b 0x18 0x61 0xeb 0xd3 0xea 0x9f 0x39 0xbc 0xda 0xa1
0xff 0x84 0xe6 0x4d 0xd1 0xf6 0xba 0x6a 0x6c 0x00 0xa2 0x48 0x23 0xcb 0x1d 0x2c
0xfc 0xcc 0x0f 0x29 0x07 0x44 0xb6 0x88 0xc3 0xdf 0x65 0x08 0x98 0x38 0x4c 0x99
0xfa 0xb2 0xa7 0x67 0x40 0x74 0xa4 0x58 0x0b 0x5b 0x7a 0x95 0x62 0x92 0xfd 0xac
0xb8 0xd8 0x2b 0x3e 0x75 0xf3 0xfb 0xf2 0x5a 0x71 0x22 0x82 0x60 0xc6 0x89 0x5e
0xe3 0x30 0x47 0x87 0x1f 0xa6 0xb7 0x6b 0x76 0x45 0xc8 0xa3 0xd2 0x42 0x8e 0x7b
0x3f 0x16 0x53 0x78 0x24 0x77 0x8f 0x6f 0xec 0xdd 0x28 0xe2 0x86 0x55 0x41 0xb3
0x05 0xd7 0x11 0x20 0x1b 0x5d 0xf9 0x26 0xa9 0x2e 0xc9 0x1e 0xc5 0xb4 0xa8 0xa0
0xfe 0x50 0x79 0xbd 0xdc 0x7f 0x37 0x04 0x81 0x0e 0x06 0x66 0x7e 0x59 0x8b 0x5f
0xe4 0x17 0x9d 0xde 0xd5 0xf8 0x2f 0x49 0x21 0xce 0x91 0x73 0x8a 0x80 0x6e 0x19
0x83 0x51 0x02 0xbe 0xc0 0xae 0x93 0xf5 0x90 0xc1 0xb0 0xb9 0xf1 0x32 0xbb 0x7d
0x5c 0xe0 0x0c 0xe5 0xc4 0x3a 0x13 0xab 0xcd 0xca 0x94 0xc2 0x3d 0x0d 0xdb 0x3c
0x6d 0xef 0xe9 0x0a 0xb1 0x97 0x1a 0xed 0x12 0xee 0x9c 0xa5 0x1c 0x4b 0x2d 0xb5
0x56 0x63 0xd0 0x68 0x01 0x64 0x70 0xd6 0xe1 0xf0 0x4e 0xd9 0x96 0x85 0x25 0x9e

SBOX2 0x270
0xcd 0xdb 0xe9 0x2f 0x45 0xd3 0xb1 0x97 0x9d 0xeb 0xd9 0x5f 0xf5 0xc3 0x41 0xa7
0x6d 0x3b 0x09 0x4f 0x65 0x33 0x11 0x77 0xfd 0x0b 0xb9 0xbf 0x55 0x63 0xe1 0x47
0x0d 0x1b 0x29 0xef 0x85 0x13 0x71 0x57 0x5d 0x2b 0x19 0x9f 0x35 0x03 0x81 0x67
0xad 0xfb 0xc9 0x8f 0xa5 0xf3 0xd1 0xb7 0x3d 0xcb 0x79 0x7f 0x95 0xa3 0x21 0x87
0xcd 0xdb 0xe9 0x2f 0x45 0xd3 0xb1 0x97 0x9d 0xeb 0xd9 0x5f 0xf5 0xc3 0x41 0xa7
0x6d 0x3b 0x09 0x4f 0x65 0x33 0x11 0x77 0xfd 0x0b 0xb9 0xbf 0x55 0x63 0xe1 0x47
0x0d 0x1b 0x29 0xef 0x85 0x13 0x71 0x57 0x5d 0x2b 0x19 0x9f 0x35 0x03 0x81 0x67
0xad 0xfb 0xc9 0x8f 0xa5 0xf3 0xd1 0xb7 0x3d 0xcb 0x79 0x7f 0x95 0xa3 0x21 0x87
0xcd 0xdb 0xe9 0x2f 0x45 0xd3 0xb1 0x97 0x9d 0xeb 0xd9 0x5f 0xf5 0xc3 0x41 0xa7
0x6d 0x3b 0x09 0x4f 0x65 0x33 0x11 0x77 0xfd 0x0b 0xb9 0xbf 0x55 0x63 0xe1 0x47
0x0d 0x1b 0x29 0xef 0x85 0x13 0x71 0x57 0x5d 0x2b 0x19 0x9f 0x35 0x03 0x81 0x67
0xad 0xfb 0xc9 0x8f 0xa5 0xf3 0xd1 0xb7 0x3d 0xcb 0x79 0x7f 0x95 0xa3 0x21 0x87
0xcd 0xdb 0xe9 0x2f 0x45 0xd3 0xb1 0x97 0x9d 0xeb 0xd9 0x5f 0xf5 0xc3 0x41 0xa7
0x6d 0x3b 0x09 0x4f 0x65 0x33 0x11 0x77 0xfd 0x0b 0xb9 0xbf 0x55 0x63 0xe1 0x47
0x0d 0x1b 0x29 0x6d 0x1d 0x5d 0xe5 0xad 0x7d 0x0d 0x65 0x2d 0x7d 0x5d 0xc5 0x0d
0x3d 0xcd 0x25 0xed 0x9d 0x9d 0xe5 0xad 0xbd 0x8d 0xa5 0x6d 0xfd 0x5d 0x45 0x8d
0xbd 0x0d 0xa5 0xed 0x9d 0xdd 0x65 0x2d 0xfd 0x8d 0xe5 0xad 0xfd 0xdd 0x45 0x8d
0xbd 0x4d 0xa5 0x6d 0x1d 0x1d 0x65 0x2d 0x3d 0x0d 0x25 0xed 0x7d 0xdd 0xc5 0x0d
0x3d 0x8d 0x25 0x6d 0x1d 0x5d 0xe5 0xad 0x7d 0x0d 0x65 0x2d 0x7d 0x5d 0xc5 0x0d
0x3d 0xcd 0x25 0xed 0x9d 0x9d 0xe5 0xad 0xbd 0x8d 0xa5 0x6d 0xfd 0x5d 0x45 0x8d
0xbd 0x0d 0xa5 0xed 0x9d 0xdd 0x65 0x2d 0xfd 0x8d 0xe5 0xad 0xfd 0xdd 0x45 0x8d
0xbd 0x4d 0xa5 0x6d 0x1d 0x1d 0x65 0x2d 0x3d 0x0d 0x25 0xed 0x7d 0xdd 0xc5 0x0d
0x3d 0x8d 0x25 0x6d 0x1d 0x5d 0xe5 0xad 0x7d 0x0d 0x65 0x2d 0x7d 0x5d 0xc5 0x0d
0x3d 0xcd 0x25 0xed 0x9d 0x9d 0xe5 0xad 0xbd 0x8d 0xa5 0x6d 0xfd 0x5d 0x45 0x8d
0xbd 0x0d 0xa5 0xed 0x9d 0xdd 0x65 0x2d 0xfd 0x8d 0xe5 0xad 0xfd 0xdd 0x45 0x8d
0xbd 0x4d 0xa5 0x6d 0x1d 0x1d 0x65 0x2d 0x3d 0x0d 0x25 0xed 0x7d 0xdd 0xc5 0x0d
0x3d 0x8d 0x25 0x6d 0x1d 0x5d 0xe5 0xad 0x7d 0x0d 0x65 0x2d 0x7d 0x5d 0xc5 0x0d
0x3d 0xcd 0x25 0xed 0x9d 0x9d 0xe5 0xad 0xbd 0x8d 0xa5 0x6d 0xfd 0x5d 0x45 0x8d
0xbd 0x0d 0xa5 0xed 0x9d 0xdd 0xe7 0xb5 0xb3 0x19 0x1f 0x8d 0xdb 0xa1 0xf7 0xc5
0xe3 0x09 0xcf 0xfd 0x2b 0xf1 0x07 0x15 0x53 0x39 0x3f 0x6d 0x3b 0x01 0xd7 0x65
0xc3 0xe9 0x2f 0x1d 0xcb 0x11 0x27 0x75 0x73 0xd9 0xdf 0x4d 0x1b 0x61 0x37 0x05
0x23 0xc9 0x0f 0x3d 0xeb 0x31 0xc7 0xd5 0x93 0xf9 0xff 0xad 0xfb 0xc1 0x17 0xa5
0x03 0x29 0xef 0xdd 0x0b 0xd1 0xe7 0xb5 0xb3 0x19 0x1f 0x8d 0xdb 0xa1 0xf7 0xc5
0xe3 0x09 0xcf 0xfd 0x2b 0xf1 0x07 0x15 0x53 0x39 0x3f 0x6d 0x3b 0x01 0xd7 0x65
0xc3 0xe9 0x2f 0x1d 0xcb 0x11 0x27 0x75 0x73 0xd9 0xdf 0x4d 0x1b 0x61 0x37 0x05
0x23 0xc9 0x0f 0x3d 0xeb 0x31 0xc7 0xd5 0x93 0xf9 0xff 0xad 0xfb 0xc1 0x17 0xa5
0x03 0x29 0xef 0xdd 0x0b 0xd1 0xe7 0xb5 0xb3 0x19 0x1f 0x8d 0xdb 0xa1 0xf7 0xc5
0xe3 0x09 0xcf 0xfd 0x2b 0xf1 0x07 0x15 0x53 0x39 0x3f 0x6d 0x3b 0x01 0xd7 0x65
0xc3 0xe9 0x2f 0x1d 0xcb 0x11 0x27 0x75 0x73 0xd9 0xdf 0x4d 0x1b 0x61 0x37 0x44

(((((((0xcd << 0x7 & 0xff) ^ 0xcd) >> 0x12 & 0xff) ^ (((0xcd << 0x7 & 0xff) ^ 0xcd))) ^ 0xd9) ^ 0x5e) ^ 0xcb) == 0x1
Do you want to keep going? (y/n/q/s) y

...
...
...

$ file Apple.dll
Apple.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows