def unchunk(self, data, raw=False): if self.args.precision > self.args.blocksize: mask = (1 << (8 * self.args.blocksize)) - 1 data = (chunk & mask for chunk in data) if not raw: return chunks.pack(data, self.args.blocksize, self.args.bigendian) def bytefilter(it): for item in it: if isinstance(item, (bytes, bytearray)): yield item else: yield bytes(item) return B''.join(bytefilter(data))
def decrypt(self, v: ByteString) -> ByteString: key = self._key v = self._unpack(v) n = len(v) r = 6 + 52 // n s = r * 0x9E3779B9 & 0xFFFFFFFF y = v[0] for _ in range(r): e = (s >> 2) & 3 for p in range(n - 1, -1, -1): z = v[(p - 1) % n] k = (p & 3) ^ e x = ((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)) ^ (s ^ y) + (key[k] ^ z) y = v[p] = v[p] - x & 0xFFFFFFFF s = s - 0x9E3779B9 & 0xFFFFFFFF return chunks.pack(v, 4)
def block_encrypt(self, block) -> BufferType: u = self._u w = self._w g = self._g r = self._r M = self._m S = self._S A, B, C, D = chunks.unpack(block, u) B = B + S[0] & M D = D + S[1] & M for i in range(1, r + 1): t = rotl(w, B * (2 * B + 1) & M, g) v = rotl(w, D * (2 * D + 1) & M, g) A = rotl(w, A ^ t, v) + S[2 * i + 0] & M C = rotl(w, C ^ v, t) + S[2 * i + 1] & M A, B, C, D = B, C, D, A A = A + S[2 * r + 2] & M C = C + S[2 * r + 3] & M return chunks.pack((A, B, C, D), u)
def block_decrypt(self, block) -> BufferType: u = self._u w = self._w g = self._g r = self._r M = self._m S = self._S A, B, C, D = chunks.unpack(block, u) C = C - S[2 * r + 3] & M A = A - S[2 * r + 2] & M for i in range(r, 0, -1): A, B, C, D = D, A, B, C t = rotl(w, B * (2 * B + 1) & M, g) v = rotl(w, D * (2 * D + 1) & M, g) C = rotr(w, C - S[2 * i + 1] & M, t) ^ v A = rotr(w, A - S[2 * i + 0] & M, v) ^ t D = D - S[1] & M B = B - S[0] & M return chunks.pack((A, B, C, D), u)
def encrypt(self, v: ByteString) -> ByteString: if not v: return v key = self._key v = list(self._unpack(v)) n = len(v) s = 0 r = 6 + 52 // n z = v[n - 1] for _ in range(r): s = s + 0x9E3779B9 & 0xFFFFFFFF e = (s >> 2) & 3 for p in range(n): y = v[(p + 1) % n] k = (p & 3) ^ e x = ((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)) ^ (s ^ y) + (key[k] ^ z) z = v[p] = v[p] + x & 0xFFFFFFFF return chunks.pack(v, 4)
def ripemd128(message): R_ = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x7, 0x4, 0xD, 0x1, 0xA, 0x6, 0xF, 0x3, 0xC, 0x0, 0x9, 0x5, 0x2, 0xE, 0xB, 0x8, 0x3, 0xA, 0xE, 0x4, 0x9, 0xF, 0x8, 0x1, 0x2, 0x7, 0x0, 0x6, 0xD, 0xB, 0x5, 0xC, 0x1, 0x9, 0xB, 0xA, 0x0, 0x8, 0xC, 0x4, 0xD, 0x3, 0x7, 0xF, 0xE, 0x5, 0x6, 0x2, ] RP = [ 0x5, 0xE, 0x7, 0x0, 0x9, 0x2, 0xB, 0x4, 0xD, 0x6, 0xF, 0x8, 0x1, 0xA, 0x3, 0xC, 0x6, 0xB, 0x3, 0x7, 0x0, 0xD, 0x5, 0xA, 0xE, 0xF, 0x8, 0xC, 0x4, 0x9, 0x1, 0x2, 0xF, 0x5, 0x1, 0x3, 0x7, 0xE, 0x6, 0x9, 0xB, 0x8, 0xC, 0x2, 0xA, 0x0, 0x4, 0xD, 0x8, 0x6, 0x4, 0x1, 0x3, 0xB, 0xF, 0x0, 0x5, 0xC, 0x2, 0xD, 0x9, 0x7, 0xA, 0xE, ] S_ = [ 0xB, 0xE, 0xF, 0xC, 0x5, 0x8, 0x7, 0x9, 0xB, 0xD, 0xE, 0xF, 0x6, 0x7, 0x9, 0x8, 0x7, 0x6, 0x8, 0xD, 0xB, 0x9, 0x7, 0xF, 0x7, 0xC, 0xF, 0x9, 0xB, 0x7, 0xD, 0xC, 0xB, 0xD, 0x6, 0x7, 0xE, 0x9, 0xD, 0xF, 0xE, 0x8, 0xD, 0x6, 0x5, 0xC, 0x7, 0x5, 0xB, 0xC, 0xE, 0xF, 0xE, 0xF, 0x9, 0x8, 0x9, 0xE, 0x5, 0x6, 0x8, 0x6, 0x5, 0xC, ] SP = [ 0x8, 0x9, 0x9, 0xB, 0xD, 0xF, 0xF, 0x5, 0x7, 0x7, 0x8, 0xB, 0xE, 0xE, 0xC, 0x6, 0x9, 0xD, 0xF, 0x7, 0xC, 0x8, 0x9, 0xB, 0x7, 0x7, 0xC, 0x7, 0x6, 0xF, 0xD, 0xB, 0x9, 0x7, 0xF, 0xB, 0x8, 0x6, 0x6, 0xE, 0xC, 0xD, 0x5, 0xE, 0xD, 0xD, 0x7, 0x5, 0xF, 0x5, 0x8, 0xB, 0xE, 0xE, 0x6, 0xE, 0x6, 0x9, 0xC, 0x9, 0xC, 0x5, 0xF, 0x8, ] def F0(x, y, z): return (x ^ y ^ z) def F1(x, y, z): return (x & y) | (z & ~x) def F2(x, y, z): return (x | (0xffffffff & ~y)) ^ z def F3(x, y, z): return (x & z) | (y & ~z) F = [F0, F1, F2, F3] K_ = [0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC] Kp = [0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x00000000] h0 = 0x67452301 h1 = 0xEFCDAB89 h2 = 0x98BADCFE h3 = 0x10325476 for block in _ripemd128_blocks(message): A_, B_, C_, D_ = h0, h1, h2, h3 Ap, Bp, Cp, Dp = h0, h1, h2, h3 for j in range(64): k = 63 - j tv = rol( A_ + F[j // 0x10](B_, C_, D_) + block[R_[j]] + K_[j // 0x10] & 0xFFFFFFFF, S_[j]) A_, D_, C_, B_ = D_, C_, B_, tv tv = rol( Ap + F[k // 0x10](Bp, Cp, Dp) + block[RP[j]] + Kp[j // 0x10] & 0xFFFFFFFF, SP[j]) Ap, Dp, Cp, Bp = Dp, Cp, Bp, tv tv = h1 + C_ + Dp & 0xFFFFFFFF h1 = h2 + D_ + Ap & 0xFFFFFFFF h2 = h3 + A_ + Bp & 0xFFFFFFFF h3 = h0 + B_ + Cp & 0xFFFFFFFF h0 = tv return chunks.pack((h0, h1, h2, h3), 4)
def keystream(self) -> Iterable[int]: key = self.args.key A: int = 0 B: int = 0 C: int = 0 S: List[int] = [0x9E3779B9] * 8 T: List[int] = [] K = list( chunks.unpack(key + bytearray(0x400 - len(key)), 4, bigendian=False)) U = 0xFFFFFFFF def _mix_state(): a, b, c, d, e, f, g, h = S a ^= (b << 0x0B) & U d = d + a & U b = b + c & U # noqa b ^= (c >> 0x02) & U e = e + b & U c = c + d & U # noqa c ^= (d << 0x08) & U f = f + c & U d = d + e & U # noqa d ^= (e >> 0x10) & U g = g + d & U e = e + f & U # noqa e ^= (f << 0x0A) & U h = h + e & U f = f + g & U # noqa f ^= (g >> 0x04) & U a = a + f & U g = g + h & U # noqa g ^= (h << 0x08) & U b = b + g & U h = h + a & U # noqa h ^= (a >> 0x09) & U c = c + h & U a = a + b & U # noqa S[:] = a, b, c, d, e, f, g, h return S def _initialize_with(R: List[int]): for i in range(0, 0x100, 8): S[:] = (x + R[j] & U for j, x in enumerate(S, i)) T[i:i + 8] = _mix_state() for _ in range(4): _mix_state() _initialize_with(K) _initialize_with(T) operations = [ (__lshift__, 0x0D), (__rshift__, 0x06), (__lshift__, 0x02), (__rshift__, 0x10), ] while True: C = (C + 1) & U B = (B + C) & U for i in range(0x100): X = T[i] shift, k = operations[i % 4] A = (A ^ shift(A, k)) & U A = (A + T[i ^ 0x80]) & U Y = T[+i] = T[X // 4 & 0xFF] + A + B & U B = K[~i] = X + T[Y // 1024 & 0xFF] & U yield from chunks.pack(K, 4, True)
def process(self, data): header = DotNetHeader(data, parse_resources=False) decompressor = lzma() class IntegerAssignment: def __init__(self, match): self.offset = match.start() self.value, = struct.unpack('<I', match[1]) def get_size(match): ins = match[1] fmt = '<B' if ins[0] == 0x1F else '<I' result, = struct.unpack(fmt, ins[-struct.calcsize(fmt):]) return result potential_seeds = [ IntegerAssignment(m) for m in re.finditer(br'\x20(....)', data, re.DOTALL) ] for entry in header.meta.RVAs: offset = header.pe.get_offset_from_rva(entry.RVA) index = struct.pack('<I', entry.Field.Index) strings_found = 0 for match in re.finditer(self._PATTERN_ARRAY_INIT % re.escape(index[:3]), data, flags=re.DOTALL): ms = match.start() def sortkey(t): weight = abs(t.offset - ms) if t.offset < ms: # this weights assignments after the array initialization down, but still # prefers them over assignments that are further away than 2kb weight += 2000 return weight size = get_size(match) if size % 0x10 or size > 10000: continue self.log_debug( F'found RVA {entry.Field.Index} initialized with length {size}.' ) potential_seeds.sort(key=sortkey) for seed in potential_seeds[1:400]: # the first potential_seed will always be the assignment of the size variable ciphertext = data[offset:offset + size * 4] key = self._xs64star(seed.value) key = chunks.pack(key, 4) + ciphertext[:-0x40] decrypted = strxor(key, ciphertext) try: decompressed = decompressor(decrypted) except Exception as e: self.log_debug( F'decompression failed for seed {seed.value:08X} at offset {seed.offset:08X}: {e}' ) continue else: self.log_info( F'decompression worked for seed {seed.value:08X} at offset {seed.offset:08X}.' ) if len(decompressed) < 0x100: continue for string in self._extract_strings(decompressed): strings_found += 1 yield string if strings_found > 10: break