| 0. 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. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. | /* * Keyfinder - finds crypto keys, encrypted data and compressed data in files * by analyzing the entropy of parts of the file. * * (c) 2005 by van Hauser / THC <[email protected]> www.thc.org * The GPL 2.0 applies to this code. * * Based on the paper "Playing hide and seek with stored keys" by Shamir and * van Someren. www.ncipher.com/products/files/papers/anguilla/keyhide2.pdf * * In my experiments I went however a different route to identify keys which * seems to be better when identifying keys. * The paper evaluates 60 byte chunks on their entropy, and depending on the * number of consecutive chunks with high entropies, this could be the key. * This tool evalutes the full key size for the entropy, increasing by an * approx 10% of the key size windows. Hence if the key is 1024 bit = 128 byte * long, the window size is 10, and the file size is 500 bytes, it looks at * the randomness from bytes 0-128, then 10-138, next 20-148 etc. * Additionally to measuring the entropy, I added checking for the * arithmetical mean, and detecting couting bytes up- and downwards in the * beginning, middle or end of the file. * By having three randomness checks and evaluating the full key size with a * sliding window, the best keyfinding measures are in place, and much better * than in the described paper. * * However still beware: you will 1) receive some false positives, and 2) * Keyfinder can not find the exact start/end region of the key, it will * usually be some bytes before or after the reported file area. * * For usage hints, type "keyfinder -h" * * To compile: gcc -o keyfinder keyfinder.c -lm * */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> #define MINIMUM_RANDOMNESS 85 #define KEY_SIZE 128 #define WINDOW_SIZE 10 #define DUMP_ROWS 16 int minimal_randomness = MINIMUM_RANDOMNESS; char *prg; int ext_entropy; int ext_mean; int debug = 0; void help() { printf("Keyfinder v1.0 (c) 2005 by van Hauser / THC <[email protected]> www.thc.org\n"); printf("\nSyntax: %s [-k KEY_SIZE] [-w WINDOW_SIZE] [-r MINIMUM_RANDOMNESS] FILE\n", prg); printf("\nOptions:\n"); printf(" -k KEY_SIZE Key size to look for (default: %d byte [%d bit])\n", KEY_SIZE, KEY_SIZE * 8); printf(" -w WINDOW_SIZE Window size to check (default: %d byte)\n", WINDOW_SIZE); printf(" -r MINIMUM_RANDOMNESS Minimum %% of randomness for keys (default: %d%%)\n", MINIMUM_RANDOMNESS); printf(" -d Print debug output\n"); printf("\nFinds binary crypto keys, crypto data and compressed data in files.\n"); printf("The result is an indicator where the key could be, not a byte exact match.\n"); printf("The randomness is calculated by the entropy, the arithmetic mean value and a\n"); printf("counting check. Read more information in the header of the keyfinder.c file.\n"); printf("Note: If -k is specified but not -w, -w will be 10%% of -k.\n"); printf("Hints: (1) the smaller -k, the smaller should be -r\n"); printf(" (2) the smaller -r the more false positives\n"); printf(" (3) -w should be 1/8 to 1/20 of -k\n"); printf(" (4) -k values are 128/256/512 byte for RSA/asymmetric keys\n"); printf(" (5) -k 512 -> -r 95; -k 128 -> -r 85 \n"); exit(-1); } /* Why is log2() in libm not working?? what a fucking #!+~$$!! */ #define log2of10 3.32192809488736234787 static double log2_(double x) { return (log2of10 * (log10(x))); } void calculate_randomness(unsigned char *buf, int buflen) { double ent = 0.0; double mean = 0.0; double datasum = 0.0; unsigned long ccount[256]; double prob[256]; int i, j = 0; for (i = 0; i < 256; i++) ccount[i] = 0; for (i = 0; i < buflen; i++) ccount[buf[i]]++; for (i = 0; i < 256; i++) { prob[i] = (double) ccount[i] / buflen; datasum += ((double) i) * ccount[i]; /**/ } for (i = 0; i < 256; i++) { if (prob[i] > 0.0) { ent += prob[i] * log2_((1.0 / prob[i])); // printf("%f += %f * %f\n", ent, prob[i], log2_((1.0 / prob[i]))); } } mean = datasum / buflen; /**/ ext_mean = (mean - 127.5) / 1.275; if (ext_mean < 0) ext_mean = ext_mean * -1; ext_mean = 100 - ext_mean; ext_entropy = (ent * 100) / 8; if (debug) { printf("Entropy: %f bits (8 is totally random)\n", ent); printf("Mean: %1.4f (127.5 is totally random)\n", mean); } if (ext_entropy + ext_mean >= minimal_randomness) { /* check for counting in the beginning */ for (i = 0; i < 8 && j == 0; i++) if (buf[i] + 1 != buf[i + 1]) j = 1; if (j == 0) j = 2; if (j == 1) j = 0; for (i = 0; i < 8 && j == 0; i++) if (buf[i] - 1 != buf[i++ + 1]) j = 1; if (j == 0) j = 2; if (j == 1) j = 0; /* check for counting in the middle */ for (i = 0; i < 8 && j == 0; i++) if (buf[((buflen/2) - i) - 4] != buf[((buflen/2) - i) - 3] + 1) j = 1; if (j == 0) j = 2; if (j == 1) j = 0; for (i = 0; i < 8 && j == 0; i++) if (buf[((buflen/2) - i) - 4] != buf[((buflen/2) - i) - 3] - 1) j = 1; if (j == 0) j = 2; if (j == 1) j = 0; /* check for counting in the end */ for (i = 1; i <= 8 && j == 0; i++) if (buf[buflen - i] != buf[(buflen - i) - 1] + 1) j = 1; if (j == 0) j = 2; if (j == 1) j = 0; for (i = 1; i <= 8 && j == 0; i++) if (buf[buflen - i] != buf[(buflen - i) - 1] - 1) j = 1; if (j == 0) j = 2; if (j == 1) j = 0; if (j == 2) { if (debug) printf("Counting detected, false positive, ignoring...\n"); ext_mean = 0; ext_entropy = 0; } } } void dump_asciihex(unsigned char *string, int length, unsigned int offset) { unsigned char *p = (unsigned char *) string; unsigned char lastrow_data[16]; unsigned int rows = length / DUMP_ROWS; unsigned int lastrow = length % DUMP_ROWS; unsigned int i, j; for (i = 0; i < rows; i++) { printf("%08hx: ", i * 16 + offset); for (j = 0; j < DUMP_ROWS; j++) { printf("%02x", p[(i * 16) + j]); if (j % 2 == 1) printf(" "); } printf(" [ "); for (j = 0; j < DUMP_ROWS; j++) { if (isprint(p[(i * 16) + j])) printf("%c", p[(i * 16) + j]); else printf("."); } printf(" ]\n"); } if (lastrow > 0) { memset(lastrow_data, 0, sizeof(lastrow_data)); memcpy(lastrow_data, p + length - lastrow, lastrow); printf("%08hx: ", i * 16 + offset); for (j = 0; j < lastrow; j++) { printf("%02x", p[(i * 16) + j]); if (j % 2 == 1) printf(" "); } while(j < DUMP_ROWS) { printf(" "); if (j % 2 == 1) printf(" "); j++; } printf(" [ "); for (j = 0; j < lastrow; j++) { if (isprint(p[(i * 16) + j])) printf("%c", p[(i * 16) + j]); else printf("."); } while(j < DUMP_ROWS) { printf(" "); j++; } printf(" ]\n"); } } void dump_found(char *buf, int key_size, unsigned int block_count, int entropy, int mean) { printf("Found at block %u (Entropy is %d%% | Mean Deviation is %d%% = %d%%):\n", block_count * 64, entropy, mean, (entropy + mean) / 2); dump_asciihex(buf, key_size, block_count * 64); printf("\n"); } int main(int argc, char *argv[]) { int key_size = KEY_SIZE; int window_size = 0; char *fn; FILE *f; char *buf; int i; int reading; unsigned int block_count = 0; prg = argv[0]; if (argc < 2 || strcmp(argv[1], "-h") == 0 || strncmp(argv[1], "--h", 3) == 0) help(); while ((i = getopt(argc, argv, "dw:r:k:")) >= 0) { switch(i) { case 'd': debug = 1; break; case 'w': window_size = atoi(optarg); break; case 'r': minimal_randomness = atoi(optarg); break; case 'k': key_size = atoi(optarg); break; default: help(); } } if (key_size != KEY_SIZE) { if (window_size == 0) window_size = (key_size / 10) - 1; } else window_size = WINDOW_SIZE; if (key_size < 20 || key_size > 65535 || window_size < 1 || window_size >= key_size || minimal_randomness < 1 || minimal_randomness > 99) { fprintf(stderr, "Error: Wrong Values! Limits: 20 < key_size < 65535; 1 < window_size < key_size; 1 < minimal_randomness < 100\n"); exit(-1); } if (key_size < window_size * 8) fprintf(stderr, "Warning: The window size is too large, -w should be 1/8 to 1/16 of -k\n"); if (optind + 1 != argc) help(); fn = argv[argc - 1]; if ((f = fopen(fn, "r")) == NULL) { fprintf(stderr, "Error: Can not open file %s\n", fn); exit(-1); } if ((buf = malloc(key_size + window_size)) == NULL) { fprintf(stderr, "Error: malloc() failed\n"); exit(-1); } memset(buf, 0, key_size + window_size); printf("Analyzing %s:\n", fn); // if (debug) printf("[Key Size: %d byte/%d bit, Window Size: %d byte, Minimal Randomness: %d%%]\n", key_size, key_size * 8, window_size, minimal_randomness); minimal_randomness = minimal_randomness * 2; if ((reading = fread(buf, 1, key_size, f)) > 0) { calculate_randomness(buf, reading); if ((ext_entropy + ext_mean) >= minimal_randomness && reading == key_size) dump_found(buf, key_size, block_count, ext_entropy, ext_mean); if (reading == key_size) reading = window_size; while (!feof(f) && reading == window_size) { if ((reading = fread(buf + key_size, 1, window_size, f)) > 0) { ++block_count; memmove(buf, buf + reading, key_size); calculate_randomness(buf, key_size); if ((ext_entropy + ext_mean) >= minimal_randomness) dump_found(buf, key_size, block_count, ext_entropy, ext_mean); } } } return 0; } |