summaryrefslogtreecommitdiffstats
path: root/dec_uUpd_xu_Atom_glp.py
blob: 8331ec23310d5fbe3decd6247306c0673d91830f (plain) (blame)
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)