DRM decrypting tool for Samsung TVs PVR
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

drmdecrypt.c 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /* drmdecrypt -- DRM decrypting tool for Samsung TVs
  2. *
  3. * Copyright (C) 2014 - Bernhard Froehlich <decke@bluelife.at>
  4. * All rights reserved.
  5. *
  6. * This software may be modified and distributed under the terms
  7. * of the GPL v2 license. See the LICENSE file for details.
  8. */
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <libgen.h>
  13. #include <limits.h>
  14. #include <unistd.h>
  15. #include "aes.h"
  16. #include "trace.h"
  17. unsigned char drmkey[0x10];
  18. char *filename(char *path, char *newsuffix)
  19. {
  20. char *end = path + strlen(path);
  21. while(*end != '.' && *end != '/')
  22. --end;
  23. if(newsuffix != NULL)
  24. strcpy(++end, newsuffix);
  25. else
  26. *end = '\0';
  27. return path;
  28. }
  29. int readdrmkey(char *mdbfile)
  30. {
  31. char tmpbuf[64];
  32. unsigned int j;
  33. FILE *mdbfp;
  34. memset(tmpbuf, '\0', sizeof(tmpbuf));
  35. if((mdbfp = fopen(mdbfile, "rb")))
  36. {
  37. fseek(mdbfp, 8, SEEK_SET);
  38. for (j = 0; j < 0x10; j++){
  39. fread(&drmkey[(j&0xc)+(3-(j&3))], sizeof(unsigned char), 1, mdbfp);
  40. }
  41. fclose(mdbfp);
  42. for (j = 0; j < sizeof(drmkey); j++)
  43. sprintf(tmpbuf+strlen(tmpbuf), "%02X ", drmkey[j]);
  44. trace(TRC_INFO, "drm key successfully read from %s", basename(mdbfile));
  45. trace(TRC_INFO, "KEY: %s", tmpbuf);
  46. return 0;
  47. }
  48. else
  49. trace(TRC_ERROR, "mdb file %s not found", basename(mdbfile));
  50. return 1;
  51. }
  52. int genoutfilename(char *outfile, char *inffile)
  53. {
  54. FILE *inffp;
  55. unsigned char inf[0x200];
  56. char tmpname[PATH_MAX];
  57. int i;
  58. if((inffp = fopen(inffile, "rb")))
  59. {
  60. fseek(inffp, 0, SEEK_SET);
  61. fread(inf, sizeof(unsigned char), 0x200, inffp);
  62. fclose(inffp);
  63. /* build base path */
  64. strcpy(tmpname, inffile);
  65. filename(tmpname, NULL);
  66. strcat(tmpname, "-");
  67. /* http://code.google.com/p/samy-pvr-manager/wiki/InfFileStructure */
  68. /* copy channel name and program title */
  69. for(i=1; i < 0x200; i += 2)
  70. {
  71. if (inf[i])
  72. {
  73. if((inf[i] >= 'A' && inf[i] <= 'z') || (inf[i] >= '0' && inf[i] <= '9'))
  74. strncat(tmpname, (char*)&inf[i], 1);
  75. else
  76. strcat(tmpname, "_");
  77. }
  78. if (i == 0xFF) {
  79. strcat(tmpname, "_-_");
  80. }
  81. }
  82. strcat(tmpname, ".ts");
  83. strcpy(outfile, tmpname);
  84. }
  85. else
  86. return 1;
  87. return 0;
  88. }
  89. int decrypt_aes128cbc(char *key, unsigned char *pin, int len, unsigned char *pout)
  90. {
  91. unsigned char IV[BLOCK_SIZE];
  92. block_state state;
  93. int i, j;
  94. memset(IV, 0, BLOCK_SIZE);
  95. memset(&state, 0, sizeof(block_state));
  96. state.rounds = 10;
  97. block_init_aes(&state, key, BLOCK_SIZE);
  98. for(i=0; i < len; i+=BLOCK_SIZE)
  99. {
  100. for(j=0; j < BLOCK_SIZE; j++)
  101. {
  102. pout[i+j] ^= IV[j];
  103. IV[j] = pin[i+j];
  104. }
  105. block_decrypt_aes(&state, pin + i, pout + i);
  106. }
  107. return 0;
  108. }
  109. /*
  110. * Decode a MPEG packet
  111. *
  112. * Transport Stream Header:
  113. * ========================
  114. *
  115. * Name | bits | byte msk | Description
  116. * ------------------------+------+----------+-----------------------------------------------
  117. * sync byte | 8 | 0xff | Bit pattern from bit 7 to 0 as 0x47
  118. * Transp. Error Indicator | 1 | 0x80 | Set when a demodulator cannot correct errors from FEC data
  119. * Payload Unit start ind. | 1 | 0x40 | Boolean flag with a value of true means the start of PES
  120. * | | | data or PSI otherwise zero only.
  121. * Transport Priority | 1 | 0x20 | Boolean flag with a value of true means the current packet
  122. * | | | has a higher priority than other packets with the same PID.
  123. * PID | 13 | 0x1fff | Packet identifier
  124. * Scrambling control | 2 | 0xc0 | 00 = not scrambled
  125. * | | | 01 = Reserved for future use (DVB-CSA only)
  126. * | | | 10 = Scrambled with even key (DVB-CSA only)
  127. * | | | 11 = Scrambled with odd key (DVB-CSA only)
  128. * Adaptation field exist | 1 | 0x20 | Boolean flag
  129. * Contains payload | 1 | 0x10 | Boolean flag
  130. * Continuity counter | 4 | 0x0f | Sequence number of payload packets (0x00 to 0x0F)
  131. * | | | Incremented only when a playload is present
  132. *
  133. * Adaptation Field:
  134. * ========================
  135. *
  136. * Name | bits | byte msk | Description
  137. * ------------------------+------+----------+-----------------------------------------------
  138. * Adaptation Field Length | 8 | 0xff | Number of bytes immediately following this byte
  139. * Discontinuity indicator | 1 | 0x80 | Set to 1 if current TS packet is in a discontinuity state
  140. * Random Access indicator | 1 | 0x40 | Set to 1 if PES packet starts a video/audio sequence
  141. * Elementary stream prio | 1 | 0x20 | 1 = higher priority
  142. * PCR flag | 1 | 0x10 | Set to 1 if adaptation field contains a PCR field
  143. * OPCR flag | 1 | 0x08 | Set to 1 if adaptation field contains a OPCR field
  144. * Splicing point flag | 1 | 0x04 | Set to 1 if adaptation field contains a splice countdown field
  145. * Transport private data | 1 | 0x02 | Set to 1 if adaptation field contains private data bytes
  146. * Adapt. field extension | 1 | 0x01 | Set to 1 if adaptation field contains extension
  147. * Below fields optional | | | Depends on flags
  148. * PCR | 33+6+9 | | Program clock reference
  149. * OPCR | 33+6+9 | | Original Program clock reference
  150. * Splice countdown | 8 | 0xff | Indicates how many TS packets from this one a splicing point
  151. * | | | occurs (may be negative)
  152. * Stuffing bytes | 0+ | |
  153. *
  154. *
  155. * See: http://en.wikipedia.org/wiki/MPEG_transport_stream
  156. */
  157. int decode_packet(unsigned char *data, unsigned char *outdata)
  158. {
  159. unsigned char iv[0x10];
  160. unsigned int i, n;
  161. int offset, rounds;
  162. int scrambling, adaptation;
  163. if(data[0] != 0x47)
  164. {
  165. trace(TRC_ERROR, "Not a valid MPEG packet!");
  166. return 0;
  167. }
  168. memcpy(outdata, data, 188);
  169. scrambling = data[3] & 0xC0;
  170. adaptation = data[3] & 0x20;
  171. if(scrambling == 0x80)
  172. {
  173. trace(TRC_DEBUG, "packet scrambled with even key");
  174. }
  175. else if(scrambling == 0xC0)
  176. {
  177. trace(TRC_DEBUG, "packet scrambled with odd key");
  178. }
  179. else if(scrambling == 0x00)
  180. {
  181. trace(TRC_DEBUG, "packet not scrambled");
  182. return 0;
  183. }
  184. else
  185. {
  186. trace(TRC_ERROR, "scrambling info seems to be invalid!");
  187. return 0;
  188. }
  189. offset=4;
  190. /* skip adaption field */
  191. if(adaptation)
  192. offset += (data[4]+1);
  193. /* remove scrambling bits */
  194. outdata[3] &= 0x3f;
  195. decrypt_aes128cbc(drmkey, data + offset, 188 - offset, outdata + offset);
  196. return 1;
  197. }
  198. void usage(void)
  199. {
  200. fprintf(stderr, "Usage: drmdecrypt [-o outfile] infile.srf\n");
  201. }
  202. int main(int argc, char *argv[])
  203. {
  204. char mdbfile[PATH_MAX];
  205. char inffile[PATH_MAX];
  206. char srffile[PATH_MAX];
  207. char outfile[PATH_MAX];
  208. FILE *srffp, *outfp;
  209. int ch, retries;
  210. int sync_find = 0;
  211. unsigned long filesize = 0, foffset = 0;
  212. unsigned long i;
  213. unsigned char buf[1024];
  214. unsigned char outdata[1024];
  215. memset(outfile, '\0', sizeof(outfile));
  216. while ((ch = getopt(argc, argv, "o:")) != -1)
  217. {
  218. switch (ch)
  219. {
  220. case 'o':
  221. strcpy(outfile, optarg);
  222. break;
  223. default:
  224. usage();
  225. exit(EXIT_FAILURE);
  226. }
  227. }
  228. if(argc == optind)
  229. {
  230. usage();
  231. exit(EXIT_FAILURE);
  232. }
  233. strcpy(srffile, argv[optind]);
  234. strcpy(inffile, srffile);
  235. filename(inffile, "inf");
  236. strcpy(mdbfile, srffile);
  237. filename(mdbfile, "mdb");
  238. /* read drm key from .mdb file */
  239. if(readdrmkey(mdbfile) != 0)
  240. return 1;
  241. if(strlen(outfile) == 0)
  242. {
  243. /* generate outfile name based on title from .inf file */
  244. if(genoutfilename(outfile, inffile) != 0)
  245. {
  246. strcpy(outfile, srffile);
  247. filename(outfile, "ts");
  248. }
  249. }
  250. trace(TRC_INFO, "Writing to %s", outfile);
  251. if((outfp = fopen(outfile, "wb")) == NULL)
  252. {
  253. trace(TRC_ERROR, "Cannot open %s for writing", outfile);
  254. return 1;
  255. }
  256. if((srffp = fopen(srffile, "rb")) == NULL)
  257. {
  258. trace(TRC_ERROR, "Cannot open %s for reading", srffile);
  259. }
  260. /* calculate filesize */
  261. fseek(srffp, 0, 2);
  262. filesize = ftell(srffp);
  263. rewind(srffp);
  264. trace(TRC_INFO, "Filesize %ld", filesize);
  265. resync:
  266. /* try to sync */
  267. sync_find = 0;
  268. retries = 10;
  269. fseek(srffp, foffset, SEEK_SET);
  270. while(sync_find == 0 && retries-- > 0)
  271. {
  272. fread(buf, sizeof(unsigned char), sizeof(buf), srffp);
  273. /* search 188byte packets starting with 0x47 */
  274. for(i=0; i < (sizeof(buf)-188-188); i++)
  275. {
  276. if (buf[i] == 0x47 && buf[i+188] == 0x47 && buf[i+188+188] == 0x47)
  277. {
  278. sync_find = 1;
  279. foffset += i;
  280. fseek(srffp, foffset, SEEK_SET);
  281. trace(TRC_INFO, "synced at offset %ld", foffset);
  282. break;
  283. }
  284. }
  285. }
  286. if (sync_find)
  287. {
  288. for(i=0; foffset+i < filesize; i+= 188)
  289. {
  290. fread(buf, sizeof(unsigned char), 188, srffp);
  291. if (buf[0] == 0x47)
  292. {
  293. decode_packet(buf, outdata);
  294. fwrite(outdata, sizeof(unsigned char), 188, outfp);
  295. }
  296. else
  297. {
  298. foffset += i;
  299. trace(TRC_WARN, "lost sync at %ld", foffset);
  300. goto resync;
  301. }
  302. }
  303. }
  304. fclose(srffp);
  305. fclose(outfp);
  306. return 0;
  307. }