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
|
import math, sys, struct, hashlib
from Crypto.Cipher import ARC4
def ROR32(v, nBit): return ((v >> nBit) | (v << (32 - nBit))) & 0xFFFFFFFF
class my_SHA256(object):
k = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
]
def __init__(self, h=None):
self.h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] if h is None else h[:]
self.w = [0]*64
def transform(self, blk):
w = self.w
w[:16] = struct.unpack_from(">16L", blk) # assert len(blk) == 0x40
for i in range(16, 64): # Extend the first 16 words into the remaining 48 words
s0 = ROR32(w[i-15], 7) ^ ROR32(w[i-15], 18) ^ (w[i-15] >> 3)
s1 = ROR32(w[i-2], 17) ^ ROR32(w[i- 2], 19) ^ (w[i-2] >> 10)
w[i] = (w[i-16] + s0 + w[i-7] + s1) & 0xFFFFFFFF
a,b,c,d,e,f,g,h = self.h
for i in range(64): # Compression function main loop
S1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25)
ch = (e & f) ^ ((0xFFFFFFFF ^ e) & g)
temp1 = (h + S1 + ch + self.k[i] + w[i]) & 0xFFFFFFFF
S0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
temp2 = (S0 + maj) & 0xFFFFFFFF
h = g
g = f
f = e
e = (d + temp1) & 0xFFFFFFFF
d = c
c = b
b = a
a = (temp1 + temp2) & 0xFFFFFFFF
# Add the compressed chunk to the current hash value:
self.h[0] = (self.h[0] + a) & 0xFFFFFFFF
self.h[1] = (self.h[1] + b) & 0xFFFFFFFF
self.h[2] = (self.h[2] + c) & 0xFFFFFFFF
self.h[3] = (self.h[3] + d) & 0xFFFFFFFF
self.h[4] = (self.h[4] + e) & 0xFFFFFFFF
self.h[5] = (self.h[5] + f) & 0xFFFFFFFF
self.h[6] = (self.h[6] + g) & 0xFFFFFFFF
self.h[7] = (self.h[7] + h) & 0xFFFFFFFF
return self.get(True)
def get(self, le=False):
return struct.pack("<8L" if le else ">8L", *self.h)
def calcEntropy(data):
entropy = 0.0
for x in range(256):
p_x = float(data.count(b"%c" % x))/len(data)
if p_x > 0: entropy -= p_x*math.log(p_x, 2)
return entropy
aX = [0xff062483, 0x9c7f0b6b, 0x2c5c83a4, 0x1e266274] # Secret
abX = struct.pack("<4L", *aX)
hPub_need = bytes.fromhex("c2e63058bef5bef84cac5319aabbac4095dd11d5f46a51cb0cfbe4641dd87a21") # Hash of Modulus
def s2i(ab):
return int(ab[::-1].hex(), 16)
def process(fn):
fmt = struct.Struct("96s32s256s4s256s")
with open(fn, "rb") as fi:
hdr30 = fi.read(0x30) # Top header
hdr, nonce, modulus, exponent, signature = fmt.unpack(fi.read(fmt.size))
_, patch_size_dw = struct.unpack_from("<28sL", hdr)
enc_size = (patch_size_dw * 4) - 0x284
enc = fi.read(enc_size) # Encrypted data of size calculated from total patch size (0x1c offset)
patch_other = fi.read()
n,e,ct = s2i(modulus), s2i(exponent), s2i(signature)
pt = bytes.fromhex(("%0512X" % pow(ct, e, n)))
rsa_pad = b"\x00\x01%s\x00" % (b"\xFF"*221) # Padding for 32 bytes of data
assert pt.startswith(rsa_pad) # Naive padding check
signed_hash = pt[-32:][::-1]
hPub = hashlib.sha256(modulus).digest()
assert hPub == hPub_need
buf = abX + nonce + abX
mh = my_SHA256()
k = b"".join(mh.transform(buf) for i in range(8))
rc4 = ARC4.new(k)
rc4.encrypt(b'\x00'*0x200) # Skip 0x200 bytes
dec = rc4.encrypt(enc)
with open(fn + ".dec", "wb") as fo: fo.write(dec)
h = hashlib.sha256(hdr + nonce + dec).digest()
hash_match = h == signed_hash
if hash_match:
print("Patch hash matched: enc size: 0x%04x" % enc_size)
else:
print("Patch hash not matched!: enc size: 0x%04x\n hash: %s\n signed_hash: %s" % \
(enc_size, h.hex(), signed_hash.hex()))
print("Patch data entropy: %f" % calcEntropy(dec))
if not hash_match:
print("XuCode is not found in patch (hash mismatch)!")
return
xu_dec_cmd_offset = -1
for dec_off in range(len(dec) - 4, 0, -1):
if dec[dec_off: dec_off+4] == b"\xfe\xff\xff\xff":
xu_dec_cmd_offset = dec_off - 0x35
break
if xu_dec_cmd_offset < 0:
print("XuCode is not found in patch (decode command is not found)!: xu_dec_cmd_offset: 0x%x" \
% xu_dec_cmd_offset)
return
xu_dec_cmd_id, xu_offset, xu_size, xu_hash = struct.unpack_from("<BLL32s", dec, xu_dec_cmd_offset)
if xu_dec_cmd_id != 0x14:
print("XuCode is not found in patch (invalid decode command id)!: xu_dec_cmd_offset: 0x%04x: cmd id: 0x%02x" \
% (xu_dec_cmd_offset, xu_dec_cmd_id))
return
patch_size = patch_size_dw * 4
assert(xu_size >= 0x20 and xu_offset >= patch_size and xu_offset + xu_size <= patch_size + len(patch_other))
xu_offset_other = xu_offset - patch_size
xu_nonce = patch_other[xu_offset_other: 0x20]
xu_data = patch_other[xu_offset_other + 0x20: xu_offset_other + xu_size]
xu_mh = my_SHA256()
xu_buf = abX + xu_nonce + abX
xu_k = b"".join(xu_mh.transform(xu_buf) for i in range(8))
xu_rc4 = ARC4.new(xu_k)
xu_rc4.encrypt(b'\x00'*0x200)
xu_dec = xu_rc4.encrypt(xu_data)
with open(fn + ".xu.dec", "wb") as fo: fo.write(xu_dec)
xu_h = hashlib.sha256(xu_nonce + xu_dec).digest()
if xu_h == xu_hash:
print("XuCode hash matched: size: 0x%04x" % xu_size)
else:
print("XuCode hash not matched!: size: 0x%04x\n hash: %s\n signed_hash: %s" % \
(xu_size, xu_h.hex(), xu_hash.hex()))
print("XuCode data entropy: %f" % calcEntropy(xu_dec))
def main(argv):
if len(argv) > 1: process(argv[1])
if __name__=="__main__": main(sys.argv)
|