checksum_bruteforce.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. #!/usr/bin/env python3
  2. """
  3. Comprehensive checksum bruteforce tool for the MEL protocol
  4. Usage: python checksum_bruteforce.py <hex_file>
  5. """
  6. import sys
  7. import struct
  8. from typing import List, Tuple, Dict
  9. def parse_hex_line(hex_string: str) -> List[int]:
  10. """Parse a hex string into list of bytes"""
  11. hex_clean = hex_string.strip().replace(' ', '')
  12. return [int(hex_clean[i:i+2], 16) for i in range(0, len(hex_clean), 2)]
  13. def bytes_to_hex(bytes_list: List[int]) -> str:
  14. """Convert bytes to hex string"""
  15. return ''.join(f'{b:02x}' for b in bytes_list)
  16. class ChecksumTester:
  17. def __init__(self):
  18. self.algorithms = [
  19. self.simple_sum,
  20. self.sum_with_carry,
  21. self.twos_complement,
  22. self.ones_complement,
  23. self.xor_checksum,
  24. self.crc16_ccitt,
  25. self.crc16_ibm,
  26. self.fletcher16,
  27. self.modsum_256,
  28. self.internet_checksum,
  29. ]
  30. def simple_sum(self, data: List[int]) -> int:
  31. """Simple sum of all bytes"""
  32. return sum(data) & 0xFFFF
  33. def sum_with_carry(self, data: List[int]) -> int:
  34. """Sum with end-around carry"""
  35. s = sum(data)
  36. while s > 0xFFFF:
  37. s = (s & 0xFFFF) + (s >> 16)
  38. return s
  39. def twos_complement(self, data: List[int]) -> int:
  40. """Two's complement of sum"""
  41. s = sum(data)
  42. return (~s + 1) & 0xFFFF
  43. def ones_complement(self, data: List[int]) -> int:
  44. """One's complement of sum"""
  45. s = sum(data)
  46. return (~s) & 0xFFFF
  47. def xor_checksum(self, data: List[int]) -> int:
  48. """XOR of all bytes, extended to 16-bit"""
  49. result = 0
  50. for b in data:
  51. result ^= b
  52. return result
  53. def crc16_ccitt(self, data: List[int], poly: int = 0x1021) -> int:
  54. """CRC-16 CCITT"""
  55. crc = 0xFFFF
  56. for byte in data:
  57. crc ^= (byte << 8)
  58. for _ in range(8):
  59. if crc & 0x8000:
  60. crc = (crc << 1) ^ poly
  61. else:
  62. crc <<= 1
  63. crc &= 0xFFFF
  64. return crc
  65. def crc16_ibm(self, data: List[int]) -> int:
  66. """CRC-16 IBM/ANSI"""
  67. return self.crc16_ccitt(data, 0x8005)
  68. def fletcher16(self, data: List[int]) -> int:
  69. """Fletcher-16 checksum"""
  70. sum1 = sum2 = 0
  71. for byte in data:
  72. sum1 = (sum1 + byte) % 255
  73. sum2 = (sum2 + sum1) % 255
  74. return (sum2 << 8) | sum1
  75. def modsum_256(self, data: List[int]) -> int:
  76. """Sum modulo 256, extended to 16-bit"""
  77. return sum(data) % 256
  78. def internet_checksum(self, data: List[int]) -> int:
  79. """Internet/TCP checksum"""
  80. # Pad to even length
  81. if len(data) % 2:
  82. data = data + [0]
  83. s = 0
  84. for i in range(0, len(data), 2):
  85. s += (data[i] << 8) + data[i+1]
  86. while s >> 16:
  87. s = (s & 0xFFFF) + (s >> 16)
  88. return (~s) & 0xFFFF
  89. def test_parametric_algorithms(data: List[int], expected: int) -> List[str]:
  90. """Test algorithms with various parameters"""
  91. matches = []
  92. # Test sum with different initial values
  93. for init_val in range(0, 0x10000, 0x1000):
  94. result = (init_val + sum(data)) & 0xFFFF
  95. if result == expected:
  96. matches.append(f"Sum + 0x{init_val:04x}")
  97. # Test sum with different modulo values
  98. for mod_val in [0xFF, 0x100, 0x101, 0x1FF, 0x200, 0xFFFF, 0x10000]:
  99. if mod_val > 0:
  100. result = sum(data) % mod_val
  101. if result == expected:
  102. matches.append(f"Sum mod 0x{mod_val:x}")
  103. # Test XOR with different patterns
  104. for pattern in [0x00, 0xFF, 0xAA, 0x55, 0x5A, 0xA5]:
  105. result = 0
  106. for byte in data:
  107. result ^= (byte ^ pattern)
  108. if (result & 0xFFFF) == expected:
  109. matches.append(f"XOR with pattern 0x{pattern:02x}")
  110. # Test rotation-based checksums
  111. for shift in range(1, 16):
  112. result = 0
  113. for byte in data:
  114. result = ((result << shift) | (result >> (16 - shift))) & 0xFFFF
  115. result ^= byte
  116. if result == expected:
  117. matches.append(f"Rotate-XOR shift {shift}")
  118. return matches
  119. def analyze_file(filename: str):
  120. """Analyze a file of hex data to find checksum patterns"""
  121. with open(filename, 'r') as f:
  122. lines = [line.strip() for line in f if line.strip()]
  123. print(f"Analyzing {filename} with {len(lines)} entries")
  124. tester = ChecksumTester()
  125. algorithm_matches = {}
  126. # Test first 10 entries to find patterns
  127. for i, line in enumerate(lines[:10]):
  128. bytes_data = parse_hex_line(line)
  129. if len(bytes_data) < 3:
  130. continue
  131. # Assume last 2 bytes are checksum
  132. payload = bytes_data[:-2]
  133. checksum_be = (bytes_data[-2] << 8) | bytes_data[-1]
  134. checksum_le = bytes_data[-2] | (bytes_data[-1] << 8)
  135. print(f"\nEntry {i}:")
  136. print(f" Payload: {bytes_to_hex(payload)}")
  137. print(f" Checksum BE: 0x{checksum_be:04x}")
  138. print(f" Checksum LE: 0x{checksum_le:04x}")
  139. # Test standard algorithms for both byte orders
  140. for checksum, order in [(checksum_be, "BE"), (checksum_le, "LE")]:
  141. print(f" Testing {order} interpretation:")
  142. for algo in tester.algorithms:
  143. result = algo(payload)
  144. if result == checksum:
  145. algo_name = f"{algo.__name__}_{order}"
  146. algorithm_matches[algo_name] = algorithm_matches.get(algo_name, 0) + 1
  147. print(f" ✓ {algo.__name__}: 0x{result:04x}")
  148. else:
  149. print(f" ✗ {algo.__name__}: 0x{result:04x}")
  150. # Test parametric algorithms
  151. param_matches = test_parametric_algorithms(payload, checksum)
  152. for match in param_matches:
  153. print(f" ✓ {match}")
  154. key = f"{match}_{order}"
  155. algorithm_matches[key] = algorithm_matches.get(key, 0) + 1
  156. # Summary of algorithms that work consistently
  157. print(f"\n{'='*50}")
  158. print("SUMMARY - Algorithms with multiple matches:")
  159. for algo, count in sorted(algorithm_matches.items(), key=lambda x: x[1], reverse=True):
  160. if count > 1:
  161. print(f" {algo}: {count} matches")
  162. return algorithm_matches
  163. def brute_force_unknown_algorithm(filename: str, max_entries: int = 5):
  164. """Brute force approach for completely unknown algorithms"""
  165. with open(filename, 'r') as f:
  166. lines = [line.strip() for line in f if line.strip()][:max_entries]
  167. print(f"Brute forcing {len(lines)} entries...")
  168. # Collect all data points
  169. data_points = []
  170. for line in lines:
  171. bytes_data = parse_hex_line(line)
  172. payload = bytes_data[:-2]
  173. checksum_le = bytes_data[-2] | (bytes_data[-1] << 8)
  174. data_points.append((payload, checksum_le))
  175. # Try to find a mathematical relationship
  176. # This is a simplified approach - in practice you'd want more sophisticated analysis
  177. # Check if it's a linear relationship: checksum = a * sum(payload) + b
  178. if len(data_points) >= 2:
  179. payload_sums = [sum(payload) for payload, _ in data_points]
  180. checksums = [checksum for _, checksum in data_points]
  181. print(f"Payload sums: {[f'0x{s:x}' for s in payload_sums[:5]]}")
  182. print(f"Checksums: {[f'0x{c:04x}' for c in checksums[:5]]}")
  183. # Try to solve for linear relationship
  184. if len(set(payload_sums)) > 1: # Need different sums to solve
  185. sum1, sum2 = payload_sums[0], payload_sums[1]
  186. check1, check2 = checksums[0], checksums[1]
  187. if sum1 != sum2:
  188. # Solve: check1 = a * sum1 + b, check2 = a * sum2 + b
  189. a = (check2 - check1) / (sum2 - sum1)
  190. b = check1 - a * sum1
  191. print(f"Testing linear relationship: checksum = {a:.3f} * sum + {b:.3f}")
  192. # Verify with all data points
  193. matches = 0
  194. for payload, expected in data_points:
  195. predicted = int(a * sum(payload) + b) & 0xFFFF
  196. if predicted == expected:
  197. matches += 1
  198. print(f" ✓ Linear match: sum={sum(payload)}, expected=0x{expected:04x}, predicted=0x{predicted:04x}")
  199. else:
  200. print(f" ✗ Linear miss: sum={sum(payload)}, expected=0x{expected:04x}, predicted=0x{predicted:04x}")
  201. if matches == len(data_points):
  202. print(f"🎉 FOUND LINEAR RELATIONSHIP! checksum = {a:.3f} * sum(payload) + {b:.3f}")
  203. def generate_test_vectors(base_hex: str, variations: int = 10):
  204. """Generate test vectors by modifying a base message"""
  205. base_bytes = parse_hex_line(base_hex)
  206. payload = base_bytes[:-2]
  207. print(f"Generating {variations} test vectors from base:")
  208. print(f"Base: {base_hex}")
  209. # Generate variations by changing single bytes
  210. for i in range(min(variations, len(payload))):
  211. modified = payload.copy()
  212. modified[i] = (modified[i] + 1) % 256 # Increment one byte
  213. # You would calculate the correct checksum here and append it
  214. # For now, we'll just show the modified payload
  215. print(f"Variation {i}: {bytes_to_hex(modified)} + [CHECKSUM_TO_CALCULATE]")
  216. def test_specific_algorithms(data: List[int], expected: int) -> Dict[str, int]:
  217. """Test specific algorithms that might be used in embedded systems"""
  218. results = {}
  219. # Test sum with various bit operations
  220. s = sum(data)
  221. # Basic variations
  222. results["sum_low16"] = s & 0xFFFF
  223. results["sum_high16"] = (s >> 16) & 0xFFFF
  224. results["sum_rotated"] = ((s << 8) | (s >> 8)) & 0xFFFF
  225. results["sum_inverted"] = (~s) & 0xFFFF
  226. results["sum_twos_comp"] = (-s) & 0xFFFF
  227. # With different initial values (common in embedded systems)
  228. for init in [0x0000, 0x5555, 0xAAAA, 0xFFFF, 0x1234, 0x4321]:
  229. results[f"sum_init_{init:04x}"] = (s + init) & 0xFFFF
  230. results[f"xor_init_{init:04x}"] = (s ^ init) & 0xFFFF
  231. # Byte-wise operations
  232. byte_xor = 0
  233. byte_sum = 0
  234. for b in data:
  235. byte_xor ^= b
  236. byte_sum += b
  237. results["byte_xor"] = byte_xor
  238. results["byte_xor_16bit"] = (byte_xor << 8) | byte_xor
  239. results["byte_sum_mod256"] = byte_sum % 256
  240. results["byte_sum_mod255"] = byte_sum % 255
  241. # Position-weighted sums
  242. pos_sum = sum(i * b for i, b in enumerate(data))
  243. results["position_weighted"] = pos_sum & 0xFFFF
  244. # Polynomial checksums (simplified)
  245. poly_result = 0
  246. for b in data:
  247. poly_result = ((poly_result << 1) ^ b) & 0xFFFF
  248. results["poly_shift_xor"] = poly_result
  249. # Return only matches
  250. return {name: value for name, value in results.items() if value == expected}
  251. def main():
  252. if len(sys.argv) != 2:
  253. print("Usage: python checksum_bruteforce.py <hex_file>")
  254. print("\nThis tool will:")
  255. print("1. Test standard checksum algorithms")
  256. print("2. Try parametric variations")
  257. print("3. Attempt to find mathematical relationships")
  258. print("4. Generate test vectors for validation")
  259. sys.exit(1)
  260. filename = sys.argv[1]
  261. try:
  262. # Main analysis
  263. print("=" * 60)
  264. print("COMPREHENSIVE CHECKSUM ANALYSIS")
  265. print("=" * 60)
  266. matches = analyze_file(filename)
  267. print("\n" + "=" * 60)
  268. print("BRUTE FORCE UNKNOWN ALGORITHMS")
  269. print("=" * 60)
  270. brute_force_unknown_algorithm(filename, max_entries=10)
  271. print("\n" + "=" * 60)
  272. print("SPECIFIC EMBEDDED ALGORITHMS")
  273. print("=" * 60)
  274. # Test first entry with specific algorithms
  275. with open(filename, 'r') as f:
  276. first_line = f.readline().strip()
  277. bytes_data = parse_hex_line(first_line)
  278. payload = bytes_data[:-2]
  279. checksum_le = bytes_data[-2] | (bytes_data[-1] << 8)
  280. specific_matches = test_specific_algorithms(payload, checksum_le)
  281. if specific_matches:
  282. print("Specific algorithm matches found:")
  283. for name, value in specific_matches.items():
  284. print(f" ✓ {name}: 0x{value:04x}")
  285. else:
  286. print("No specific algorithm matches found")
  287. print("\n" + "=" * 60)
  288. print("TEST VECTOR GENERATION")
  289. print("=" * 60)
  290. generate_test_vectors(first_line, 5)
  291. except FileNotFoundError:
  292. print(f"Error: File '{filename}' not found")
  293. except Exception as e:
  294. print(f"Error: {e}")
  295. if __name__ == "__main__":
  296. main()