/
macwars-D-decrypt.py
executable file
·199 lines (171 loc) · 5.8 KB
/
macwars-D-decrypt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python3
import sys
ror = lambda val, r_bits, max_bits: \
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
def getkey1(start,end):
key = int("0x50414345",0)
i = start + 0x80
while(i<=(end + 0x80)):
fh.seek(i);
ib = int.from_bytes(fh.read(1),byteorder='big')
key+=ib
i+=1
return key
def get_bit(i):
i=i%8
return (0x1 << i)
def getkey2(key,start,end,inflag,obfuscator):
i = start
while(i<=end):
bitnum = 0x80
if inflag==0:
bitnum=0x1
fh.seek(i+0x80);
ib = int.from_bytes(fh.read(1),byteorder='big')
while True:
key = key << 1
test_mask=get_bit(bitnum)
if key<(2**32):
if((test_mask & ib)==0):
key = key ^ obfuscator
else:
key = key & 0xFFFFFFFF
if((test_mask & ib)!=0):
key = key ^ obfuscator
if (inflag!=0):
bitnum = ror(bitnum,1,8)
if ((bitnum & 0x80)!=0):
break
else:
bitnum = rol(bitnum,1,8)
if ((bitnum & 0x1)==0x1):
break
i+=1
return key
def write_decrypt1(start,end,key):
fh.seek(start+0x80)
inblock=bytearray(fh.read(end-start+1))
outblock=decrypt1(key,inblock[:])
fh.seek(start+0x80)
fh.write(bytes(outblock))
def decrypt1(key,block):
def rotate_key(d0,d1,d2):
if d2==0:
d0 = ror(d0,d1,32)
else:
d0 = rol(d0,d1,32)
return d0
i=0
while i<len(block):
key_lb = key&0xFF
i_lb = i&0xFF
if (i_lb&0x1 == 0):
key_lb = key_lb & 0xF
else:
key_lb = (key_lb >> 4) &0xFFFF
key = rotate_key(key,key_lb,0)
ib = block[i]
key_lb = key&0xFF
output = ib ^ key_lb
block[i]=output
i+=1
return block
def write_decrypt2(i,end,key,salt):
fh.seek(i+0x80)
block=fh.read(end-i+1)
fh.seek(i+0x80)
fh.write(decrypt2(block,key,salt))
def swap(inbyte):
in_th = inbyte>>16
in_bh = (inbyte & 0xFFFF)
out = (in_bh << 16) + in_th
return out
def decrypt2(block,key,salt):
blockarray=bytearray(block)
i=0
while(i<len(blockarray)):
d2=key
d4=key
d2=swap(d2)
key+=d2
key=key & 0xFFFF
d4=swap(d4)
d4=d4 & 0xFFFF0000
key+=d4
key=swap(key)
key+=salt
key=key&0xFFFFFFFF
ib = blockarray[i]
output=ib ^ (key & 0xFF)
blockarray[i]=output
i+=1
return bytes(blockarray)
def copybytes(source,target,count):
fh.seek(source+0x80)
block=fh.read(count)
fh.seek(target+0x80)
fh.write(block)
def savebytes(filename,start,end):
fh.seek(start+0x80)
oh=open(filename,"wb")
oh.write(fh.read(end-start+1))
oh.close()
fh = open("D.bin","r+b")
#round 1
key= getkey1(0x5204,0x5369) # 0x5234
key2 = getkey1(0x5204,0x6d19) # 0x5220
write_decrypt1(0x536a,0x6d88-1,key) # 0x524C
#round 2
key= getkey2(0,0x5204,0x56b5,0,0x8005) #0x539E
key2 = key2 + getkey2(0x50414345,0x5204,0x6d19,0xFF,0x8005) #0x5406
write_decrypt2(0x56b6,0x6d80-1,key,0x776de01d) # 0x5424
#round 3
key= (getkey2(0xAAAAAAAA,0x5204,0x6535,0,0x1021) + 0xF085) & 0xFFFFFFFF #0x5738
# the 0xf085 is loaded in at 0x56c8
key2 = key2 + getkey2(0x50414345,0x5204,0x6d19,0xFF,0x1021) # 0x595C-0x5960
write_decrypt2(0x6536,0x6d7f-1,key,0xdddaac4d) # 0x598E
#round 4 (jump table and code tables)
key= getkey2(0x55555555,0x536a,0x6d19,0xFF,0x8005) # 0x65F4
key = (key + key2 + getkey2(0x50414345,0x6536,0x6d19,0x0,0x8005)) & 0xFFFFFFFF # 0x65CE-0x65D2
#the entire jump table was encrypted, then the first 8 bytes stored in CODE4.
#then an entry jumping to the PACE code was put in.
#round4 copies these 8 bytes out of CODE4, restoring the entire applications
#jump table and then decrypts it
#0x66F0 says this is stored in CODE4
#0x65ac loads it into memory and copies it over the jump
copybytes(0x6d8f,0x114,0xD-0x5)
#decryption happens at 0x6654
write_decrypt2(0x114,0x114+0x88-1,key,0xd975bb1d)
#bruteforce this instead of stepping through all the code
#between 0x57b0 and 0x492F to figure out what to add at 0x669a
#basically, the first byte of a code segment must be \x00, unless
#the jump table is over 256 bytes (which it isn't in this case)
#so loop until the \x00 has been found.
#this only works because the encryption algorithm really only uses the last
#single byte of the key...so really there are only 256 possibilities.
#no idea why PACE didnt use all 32 bits available to them; it would have
#been much more difficult
i=0
fh.seek(0x1a0 + 0x80) #use the first byte of CODE1 as the target
testblock=bytearray(fh.read(1))
while i<0xFF:
testkey = (key+i)&0xFFFFFFFF
outblock=decrypt2(testblock[:],testkey,0xad95322d)
if outblock[0]==0:
print("Key add:",hex(i),outblock)
break
i+=1
key=testkey
#with this key, PACE now decrypts anything in memory at 0x66AE
#and then overloads the trap to load a new code segment to decrypt
#them on the fly at 0x66C6. This way, it still works when the Macintosh
#memory manager swaps code segments in from disk and out from memory.
write_decrypt2(0x1a0,0x1a0+0x4544-1,key,0xad95322d) #CODE1
write_decrypt2(0x46e8,0x46e8+0x3a4-1,key,0xad95322d) #CODE2
write_decrypt2(0x4a90,0x4a90+0x570-1,key,0xad95322d) #CODE3
write_decrypt2(0x5004,0x5004+0x46-1,key,0xad95322d) #CODE255
fh.close()