Hacking CA system challenge

Messages
44
By browsing _http://en.kingofsat.net/pos-4.8E.php I saw at 12379 H 27500 a CA system called "Compel".
This CA system was unknown to me, so I have recorded the TS and looked at the scrambled packets.
Finally I was able to decrypt the video/audio packets.
This is the easiest CA system I have ever seen.
No Compel firmware or receiver was necessary, just the encrypted stream.

I guess on this forum there are some people that are also interested in a try to hack a CA system.
So why not try to do it with this one.

May be one would try it but can't receive sat position 4.8E, so I have recorded a full TS stream of 12379 H 27500 for you:
_https://mega.nz/#!pBIRVK7J!PdxwsJiusYXHJSyYrvI7NHgtGzp1UXT8yW5gV6-BZ4M

This stream is 153 seconds long and contains besides other channels the following Compel encrypted channels:
ATG Live (ATG EU BSS)
Kanal 75 (K75 International)

After the decryption I have taken two pictures of these channels at the beginning of the stream:

ATG:
34113557557de000873.png

K75
34113557557de017dc8.png



If you was successful you can also show us a picture of the stream if you want.


You should know..
- the functions of the 4 byte TS header.
- the Adaptation field (AF) field. Not all the function that an AF can have, only if it is used as filler when the payload is smaller than 184 bytes.
- how the first 3 payload bytes of decrypted/plain TS packet looks like.
...


Let us focus on ATG for this challenge:
PMT PID is D2h
Video PID is 1022h

Is the channel crypted with only one key type or is it crypted with an alternating ODD EVEN key?
Is an Compel EMM in the CAT PID present? If yes, what's the EMM PID?
Is an Compel ECM in the PMT PID present? If yes, what's the ECM PID?

Search the first encrypted video packet that has the Payload Unit Start Indicator (PUSI) bit set and that don't have an AF.
Post the hex dump of this 188 byte TS packet here.

Do you notice something unusual when you look at this encrypted hex dump?
 

xosef1234

Registered
Messages
107
Hi Colibri
good to hear/read from you! :thum:

Post the hex dump of this 188 byte TS packet here.
Code:
47 50 22 db 00 00 01 e0 00 00 8b 80 3a 2f 94 b0 92 65 3f 3f 01 00 00 20 c0 c4 87 3f 00 3e b5 bd 0d 0c a3 3f 3f 00 3f 00 3f 01 01 63 5e f9 fb 8f 83 66 f8 f2 6d 55 51 80 f0 ef 64 c0 fa d8 0f 69 00 27 3c 8e bd eb a0 01 ab 5e 9e e0 00 ba be ec bf 79 a1 56 41 5d 2a eb a4 30 10 21 38 93 d7 74 24 3f 7d 5c d3 03 84 60 d8 a3 b8 19 40 fe f3 d8 01 c8 57 1a 3f 06 06 bf 3e b0 a0 60 ca 00 36 b4 c6 89 1f 1f c0 0d 8e 9b ea b2 82 25 05 e5 f9 cc 3f c9 3e 71 9b 47 f9 f1 00 c9 3f f6 cc e8 18 80 06 01 fc 7f 01 80 3e a4 a3 7a 03 30 c0 c6 c0 00 00 01 02 63 62 8a 0c 8a 49 aa 34 85
Maybe I am wrong but it looks like it is not encrypted at all. The first 3 payload bytes seem to be unencrypted?!

Regarding EMM and ECM I guess the pids are 1026h and 1028h, but I couldn't find those in CAT and PMT.

Maybe tomorrow more ....
 

[0_o]

Registered
Messages
124
So good to see you again Colibri!
Just saying Hi, because I dont have 5E here to play around too! :(
 
Messages
44
The posted hex dump is correct.
If it's encrypted then it's not CSA.
After I have seen the "00 00 01" in a packet that is marked in the header as encrypted I have changed the encryption flag in the headers from crypt to plain and checked if the VLC player is able to play it now.
So this could be the next step to see if it's that easy. So someone should try it and report the result.

Yes, the compel control stream PID is not present in the CAT/PMT.

Extract from the manual:
“compel method dvbpid” - This is the default control method. Compel will control the unit by sending commands in the MPEG transport stream on the the tuned RF carrier. With this method, the PID of the Compel commands must be set in the unit by issuing the command: “compel pid 1026”. The PID number (1026) is expressed in hexadecimal. This setting must match the setting in the Compel control system.

For the hack the PIDs 1026h and 1028h aren't needed (only video/audio PIDs).

Even if someone can't receive 5E it's still possible to participate if you want, because you can download the stream via the link in my first post.
 

C0der

Registered
Messages
267
alternating ODD EVEN key
no EMM other than VIA and N*S
no ECM

Could be a simple xor with 3f on some bytes.
 
Messages
44
Yes, alternating ODD EVEN key.
The 153 seconds stream has 3 crypto periods. So one compel crypto preiod is ~51 seconds long.

Yes, it's just a simple xor with 3Fh on some bytes.
But the key is new for every crypto period and never repeats.
I have recorded a very long stream with 1500 crypto periods.
It was possible to find the key for every single crypto period.

Here a real example of seven 184 byte keys (but they don't match the example TS stream I have uploaded):
Code:
00 00 00 00 00 00 00 00 3F 00 3F 3F 00 3F 00 3F 3F 00 00 00 00 3F 00 00 3F 3F 3F 00 00 3F 3F 3F 3F 00 3F 00 3F 3F 3F 3F 3F 00 00 00 00 3F 00 3F 3F 00 3F 3F 3F 3F 3F 3F 00 00 00 00 3F 3F 00 00 3F 00 00 3F 3F 3F 00 00 00 00 00 00 3F 3F 3F 00 00 00 3F 00 00 3F 3F 3F 00 3F 3F 00 3F 3F 00 00 3F 00 3F 00 00 3F 00 3F 3F 00 00 3F 3F 3F 3F 3F 3F 00 00 00 3F 3F 3F 3F 3F 00 3F 00 3F 3F 3F 3F 00 3F 00 3F 3F 3F 3F 3F 00 00 00 3F 3F 00 3F 00 00 3F 00 00 00 00 00 3F 00 00 00 3F 00 00 3F 3F 3F 3F 00 00 00 3F 00 3F 00 3F 00 00 3F 3F 00 00 3F 00 00 3F 3F 3F 3F 3F 
00 00 00 00 00 00 00 00 00 00 00 00 3F 00 00 00 3F 00 3F 3F 3F 00 3F 3F 00 00 00 3F 00 00 00 3F 00 3F 00 00 00 3F 3F 3F 3F 3F 3F 00 3F 3F 00 3F 3F 00 3F 00 3F 3F 00 00 3F 00 3F 00 3F 00 00 3F 3F 00 00 3F 3F 3F 3F 00 3F 00 00 3F 00 3F 00 00 3F 3F 00 3F 00 3F 00 3F 00 3F 00 3F 3F 3F 3F 3F 3F 00 3F 00 3F 3F 00 3F 00 3F 00 3F 3F 00 00 3F 3F 3F 3F 00 3F 00 00 3F 00 3F 00 00 3F 3F 00 00 00 00 3F 3F 3F 00 00 3F 00 00 3F 00 3F 00 3F 00 00 3F 3F 3F 3F 3F 00 00 00 3F 3F 3F 00 3F 3F 00 3F 00 00 00 00 00 00 00 3F 3F 00 3F 00 3F 00 00 00 00 3F 00 3F 00 00 3F 
00 00 00 00 00 00 00 00 00 00 3F 3F 00 3F 3F 00 3F 3F 3F 00 3F 3F 00 00 3F 00 3F 00 00 3F 3F 3F 00 00 3F 3F 3F 00 3F 3F 3F 3F 3F 3F 00 3F 3F 00 3F 3F 3F 00 00 3F 00 00 3F 00 3F 3F 00 3F 3F 3F 3F 00 3F 3F 00 3F 00 3F 3F 00 00 00 3F 3F 00 3F 00 00 3F 00 00 3F 00 00 00 3F 00 00 00 3F 3F 00 00 3F 00 3F 3F 00 00 3F 00 00 3F 00 3F 00 3F 3F 3F 00 00 3F 3F 3F 00 00 00 3F 3F 00 3F 3F 00 00 00 3F 00 00 00 3F 00 00 00 00 00 00 00 3F 00 00 00 3F 00 3F 00 3F 00 00 3F 3F 00 00 3F 00 3F 3F 3F 3F 00 3F 3F 00 00 3F 00 00 00 00 00 3F 00 00 00 3F 00 00 00 3F 00 3F 
00 00 00 00 00 00 00 00 00 00 3F 00 00 3F 00 00 3F 3F 00 00 00 3F 3F 00 3F 00 3F 3F 3F 00 00 00 00 3F 3F 00 3F 00 3F 00 00 00 3F 00 3F 00 3F 3F 00 3F 3F 3F 3F 3F 3F 00 00 00 3F 00 3F 00 3F 00 00 00 00 3F 00 3F 3F 00 3F 3F 00 00 00 3F 00 00 3F 3F 3F 00 3F 3F 00 3F 3F 3F 3F 00 3F 00 3F 00 00 3F 00 3F 00 3F 00 00 3F 00 3F 3F 3F 00 3F 00 00 00 00 3F 3F 00 3F 00 00 00 3F 3F 3F 00 00 3F 00 3F 00 00 3F 3F 00 3F 3F 00 00 00 3F 3F 00 3F 3F 3F 00 3F 00 00 3F 00 3F 3F 00 00 00 00 3F 3F 3F 00 00 00 00 00 3F 3F 3F 3F 00 3F 00 00 3F 00 3F 00 00 3F 3F 00 00 3F 
00 00 00 00 00 00 00 00 3F 3F 00 3F 3F 00 3F 3F 00 00 3F 00 00 00 3F 3F 3F 00 00 3F 00 3F 3F 00 3F 00 3F 3F 3F 00 3F 3F 3F 3F 3F 3F 3F 3F 3F 00 00 3F 3F 3F 3F 00 3F 00 3F 3F 3F 3F 3F 3F 3F 3F 00 00 00 00 3F 3F 00 00 3F 3F 3F 3F 00 3F 3F 3F 3F 00 3F 00 00 00 3F 00 00 3F 3F 3F 3F 00 3F 00 3F 3F 3F 3F 3F 00 3F 3F 00 3F 3F 3F 3F 00 3F 00 3F 3F 00 00 00 3F 3F 00 3F 3F 00 00 3F 00 00 3F 3F 3F 3F 00 00 00 3F 3F 3F 00 00 00 3F 3F 3F 3F 00 00 00 3F 3F 3F 00 3F 00 3F 00 00 3F 3F 00 3F 3F 00 00 3F 3F 00 3F 3F 00 3F 00 00 00 00 00 00 3F 3F 3F 00 3F 00 00 00 
00 00 00 00 00 00 00 00 3F 3F 3F 00 3F 00 00 3F 3F 3F 3F 3F 3F 3F 00 00 00 00 3F 3F 3F 00 3F 3F 3F 00 00 00 3F 00 3F 3F 3F 3F 3F 00 3F 3F 00 3F 00 3F 00 00 3F 00 3F 00 3F 3F 00 00 00 00 00 3F 3F 3F 00 00 00 3F 3F 3F 3F 00 00 00 3F 3F 3F 3F 00 00 3F 00 3F 3F 00 3F 00 3F 3F 00 00 00 3F 3F 3F 3F 3F 00 3F 00 3F 3F 3F 3F 00 3F 00 00 00 3F 3F 3F 00 00 3F 00 3F 00 3F 00 3F 00 3F 00 3F 3F 3F 00 00 00 00 3F 3F 00 00 00 00 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 00 00 00 3F 3F 3F 3F 00 00 3F 3F 00 3F 00 3F 3F 3F 00 3F 00 3F 00 00 00 00 00 00 3F 3F 3F 3F 00 3F 3F 00 
00 00 00 00 00 00 00 00 3F 3F 00 3F 3F 00 3F 00 3F 3F 3F 3F 3F 3F 3F 3F 00 3F 00 00 3F 3F 3F 3F 00 00 00 00 00 3F 3F 3F 3F 00 00 00 3F 00 00 3F 00 3F 00 00 00 00 3F 00 3F 00 3F 3F 00 00 00 00 3F 00 00 00 3F 3F 00 3F 3F 00 00 00 00 00 3F 3F 3F 00 3F 3F 3F 00 00 3F 00 00 00 00 00 00 3F 00 00 3F 3F 00 00 3F 00 00 3F 3F 00 3F 00 3F 3F 3F 00 00 00 3F 00 3F 3F 00 3F 00 3F 00 00 00 00 3F 00 00 3F 00 00 00 00 3F 3F 00 00 00 3F 3F 00 00 00 3F 00 3F 00 00 3F 00 3F 3F 3F 00 00 00 3F 3F 3F 3F 3F 3F 3F 00 00 00 00 3F 3F 00 3F 00 00 3F 00 00 00 00 00 3F 00 00

For the 1500 keys I saw that the first 8 bytes of the 184 byte key is always 00h (so never encrypted).
So it seems the real key length is 176 bytes (B0h) long for the last 176 bytes of the TS packet.

But how can the key for a crypto period be found?
 

C0der

Registered
Messages
267
First guess:
Look at enough packets.
If the byte at position x is 3f more often than 00, its 3f in the "key".

Next guess:
Look at the last packet of the pes packets and hope for alot of 00.
 
Last edited:

kebien

Registered
Messages
1,329
Transport scrambling control is always 11 (3)
You mean you changed this to show as not scrambled?
Is VLC showing is DRM if this bits are set?
 

JimBizkit

Registered
Messages
128
yeah, the 00 idea seems good, but instead use FF: search for patterns of FF and C0 (= FF xor 3F)

Code:
00007932h: FF FF FF FF FF FF C0 FF FF C0 FF FF C0 C0 FF FF ; ÿÿÿÿÿÿÀÿÿÀÿÿÀÀÿÿ
00007942h: FF C0 C0 C0 C0 C0 FF C0 FF C0 C0 C0 C0 C0 C0 FF ; ÿÀÀÀÀÀÿÀÿÀÀÀÀÀÀÿ
00007952h: C0 FF C0 FF FF FF FF FF FF FF C0 C0 FF FF C0 C0 ; ÀÿÀÿÿÿÿÿÿÿÀÀÿÿÀÀ
00007962h: C0 C0 C0 C0 FF C0 FF C0 C0 FF FF C0 FF FF FF FF ; ÀÀÀÀÿÀÿÀÀÿÿÀÿÿÿÿ
00007972h: C0 FF C0 C0 C0 FF C0 C0 C0 C0 C0 C0 FF FF C0 C0 ; ÀÿÀÀÀÿÀÀÀÀÀÀÿÿÀÀ
00007982h: FF FF C0 C0 FF FF C0 FF C0 FF FF C0 FF FF C0 FF ; ÿÿÀÀÿÿÀÿÀÿÿÀÿÿÀÿ
00007992h: C0 C0 C0 C0 FF FF C0 FF FF FF FF C0 C0 FF C0    ; ÀÀÀÀÿÿÀÿÿÿÿÀÀÿÀ

also:
the data in pid 1028 has a length of AF, they key has length B0, .. could just be a coincidence, but maybe one can discover more when comparing the current key with these packets
 
Last edited:

C0der

Registered
Messages
267
Code:
00 00 00 00 00 00 00 00 3F 00 00 3F
00 00 3F 3F 00 00 00 3F 3F 3F 3F 3F 00 3F 00 3F
3F 3F 3F 3F 3F 00 3F 00 3F 00 00 00 00 00 00 00
3F 3F 00 00 3F 3F 3F 3F 3F 3F 00 3F 00 3F 3F 00
00 3F 00 00 00 00 3F 00 3F 3F 3F 00 3F 3F 3F 3F
3F 3F 00 00 3F 3F 00 00 3F 3F 00 00 3F 00 3F 00
00 3F 00 00 3F 00 3F 3F 3F 3F 00 00 3F 00 00 00
00 3F 3F 00 3F 00 00 3F 3F 3F 3F 00 00 00 00 00
3F 3F 3F 00 00 3F 00 00 3F 00 3F 00 3F 3F 00 3F
3F 3F 3F 00 3F 3F 00 00 00 3F 3F 00 3F 00 00 00
00 3F 00 3F 3F 00 00 3F 3F 3F 00 3F 3F 00 00 00
00 00 00 00 3F 3F 00 00 00 00 00 3F
 

JimBizkit

Registered
Messages
128


some poc code: (not perfect, just a proof of concept for those that want to do the challenge and are stuck):

Code:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE //needed for PTHREAD_MUTEX_RECURSIVE on some plattforms and maybe other things; do not remove
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

#define SCT_LEN(sct) (3+((sct[1]&0x0f)<<8)+sct[2])

#define EMU_STREAM_SERVER_MAX_CONNECTIONS 8
#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 16

	typedef struct 
	{
		uint32_t pvu_des_ks[8][2][32];
		int8_t pvu_csa_used;
		void* pvu_csa_ks[8];
	} emu_stream_client_key_data;

	typedef struct 
	{
		int8_t have_pat_data;
		int8_t have_pmt_data;
		int8_t have_ecm_data;
		uint8_t data[1024+208];
		uint16_t data_pos;
		uint16_t srvid;
		uint16_t pmt_pid;
		uint16_t ecm_pid;
		uint16_t video_pid;
		uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS];
		uint8_t audio_pid_count;
		int16_t ecm_nb;
		emu_stream_client_key_data key;
	} emu_stream_client_data;


uint32_t b2i(int32_t n, const uint8_t *b)
{
	switch(n) {
	case 2:
		return  (b[0] <<  8) |  b[1];
	case 3:
		return  (b[0] << 16) | (b[1] <<  8) |  b[2];
	case 4:
		return ((b[0] << 24) | (b[1] << 16) | (b[2] <<8 ) | b[3]) & 0xffffffff;
	default:
		return 0;
	}
	return 0;
}

uint8_t *i2b_buf(int32_t n, uint32_t i, uint8_t *b)
{
	switch(n) {
	case 2:
		b[0] = (i**** 8) & 0xff;
		b[1] = (i    ) & 0xff;
		break;
	case 3:
		b[0] = (i****16) & 0xff;
		b[1] = (i**** 8) & 0xff;
		b[2] = (i    ) & 0xff;
	case 4:
		b[0] = (i****24) & 0xff;
		b[1] = (i****16) & 0xff;
		b[2] = (i**** 8) & 0xff;
		b[3] = (i    ) & 0xff;
		break;
	}
	return b;
}

static void SearchTsPackets(uint8_t *buf, uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset)
{
	uint32_t i;
	
	(*packetSize) = 0;
	(*startOffset) = 0;

	for(i=0; i<bufLength; i++) {	
		if(buf[i] == 0x47) {
			if((buf[i+188] == 0x47) & (buf[i+376] == 0x47)) {  //if three packets align, probably safe to assume correct size.
				(*packetSize) = 188;
				(*startOffset) = i;
				return;
			}
			else if((buf[i+204] == 0x47) & (buf[i+408] == 0x47)) {
				(*packetSize) = 204;
				(*startOffset) = i;
				return;
			}
			else if((buf[i+208] == 0x47) & (buf[i+416] == 0x47)) {
				(*packetSize) = 208;
				(*startOffset) = i;
				return;
			}					
		}	
	}
}

typedef void (*ts_data_callback)(emu_stream_client_data *cdata);

static void ParseTSData(uint8_t table_id, uint8_t table_mask, uint8_t min_table_length, int8_t* flag, uint8_t* data,
							uint16_t data_length, uint16_t* data_pos, int8_t payloadStart, uint8_t* buf, int32_t len,
							ts_data_callback func, emu_stream_client_data *cdata)
{
	uint16_t section_length;
	int32_t i;
	int8_t found_start = 0;
	uint16_t offset = 0;
	int32_t free_data_length;
	int32_t copySize;
	
	if(len < 1)
		{ return; }
	
	if(*flag == 0 && !payloadStart)
		{ return; }

	if(*flag == 0)
	{ 
		*data_pos = 0;
		 offset = 1 + buf[0];
	}
	else if(payloadStart)
	{ 
		offset = 1; 
	}
	
	if(len-offset < 1)
		{ return; }
	
	free_data_length = data_length - *data_pos;
	copySize = (len-offset) > free_data_length ? free_data_length : (len-offset);
	
	memcpy(data+(*data_pos), buf+offset, copySize);	
	(*data_pos) += copySize;

	found_start = 0;
	for(i=0; i < *data_pos; i++)
	{
		if((data[i] & table_mask) == table_id)
		{
			if(i != 0)
			{
				if((*data_pos)-i > i)
					{ memmove(data, &data[i], (*data_pos)-i); }
				else
					{ memcpy(data, &data[i], (*data_pos)-i); }
			
				*data_pos -= i;
			}
			found_start = 1;
			break;
		}	
	}
	if(!found_start)
		{ *flag = 0; return; }

	*flag = 2;

	if(*data_pos < 3)
		{ return; }

	section_length = SCT_LEN(data);

	if(section_length > data_length || section_length < min_table_length)
		{ *flag = 0; return; }
	
	if((*data_pos) < section_length)
		{ return; }

	func(cdata);
	
	found_start = 0;
	for(i=section_length; i < *data_pos; i++)
	{
		if((data[i] & table_mask) == table_id)
		{
			if((*data_pos)-i > i)
				{ memmove(data, &data[i], (*data_pos)-i); }
			else
				{ memcpy(data, &data[i], (*data_pos)-i); }
			
			*data_pos -= i;
			found_start = 1;
			break;
		}	
	}	
	if(!found_start)
		{ *data_pos = 0; }
	
	*flag = 1;
}

static void ParsePATData(emu_stream_client_data *cdata)
{
	uint8_t* data = cdata->data;
	uint16_t section_length = SCT_LEN(data);
	uint16_t srvid;
	int32_t i;

	for(i=8; i+7<section_length; i+=4)
	{
		srvid = b2i(2, data+i);
		
		if(srvid == 0)
			{ continue; }
		
		if(cdata->srvid == srvid)
		{
			cdata->pmt_pid = b2i(2, data+i+2) & 0x1FFF;
			printf("[Emu] stream found pmt pid: %X\n", cdata->pmt_pid);
			break;
		}
	}
}

static void ParsePMTData(emu_stream_client_data *cdata)
{
	uint8_t* data = cdata->data;
	
	uint16_t section_length = SCT_LEN(data);
	int32_t i;
	uint16_t program_info_length = 0, es_info_length = 0;
	uint8_t descriptor_tag = 0, descriptor_length = 0;
	uint8_t stream_type;
	uint16_t stream_pid, caid;
	
	program_info_length = b2i(2, data+10) &0xFFF;
	
	if(12+program_info_length >= section_length)
		{ return; }
	
	for(i=12; i+1 < 12+program_info_length; i+=descriptor_length+2)
	{
		descriptor_tag = data[i];
		descriptor_length = data[i+1];
		
		if(descriptor_length < 1)
			{ break; }
			
		if(i+1+descriptor_length >= 12+program_info_length)
			{ break; }
		
		if(descriptor_tag == 0x09 && descriptor_length >= 4)
		{
			caid = b2i(2, data+i+2);
			
			if(caid****8 == 0x0E)
			{
		    	cdata->ecm_pid = b2i(2, data+i+4) &0x1FFF;
		    	printf("[Emu] stream found ecm_pid: %X\n", cdata->ecm_pid);
		    	break;
		    }
		}
	}
		
	for(i=12+program_info_length; i+4<section_length; i+=5+es_info_length)
	{
		stream_type = data[i];
		stream_pid = b2i(2, data+i+1) &0x1FFF;
		es_info_length = b2i(2, data+i+3) &0xFFF;
		
		if(stream_type == 0x01 || stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B 
			|| stream_type == 0x24 || stream_type == 0x42 || stream_type == 0x80 || stream_type == 0xD1 
			|| stream_type == 0xEA)
		{ 
			cdata->video_pid = stream_pid;
			printf("[Emu] stream found video pid: %X\n", stream_pid);
		}
		
		else if(stream_type == 0x03 || stream_type == 0x04 || stream_type == 0x0F || stream_type == 0x11 
			|| stream_type == 0x81 || stream_type == 0x87)
		{
			if(cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS)
				{ continue; }
			
			cdata->audio_pids[cdata->audio_pid_count] = stream_pid;
			cdata->audio_pid_count++;
			printf("[Emu] stream found audio pid: %X\n", stream_pid);
		}
	}
}

static void ParseECMData(emu_stream_client_data *cdata)
{
	uint8_t* data = cdata->data;
	uint16_t section_length = SCT_LEN(data);
	uint8_t dcw[16];
	
	if(section_length < 0xb)
		{ return; }

	if(data[0xb] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[0xb] == 0)
		|| ((cdata->ecm_nb - data[0xb]) > 5))
	{
		cdata->ecm_nb = data[0xb];

		//ParseECM(data, dcw, &cdata->key);
	}
}

void cs_log_txt(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len);
void cs_log_hex(const uint8_t *buf, int32_t n, const char *fmt, ...) __attribute__((format(printf, 3, 4)));

#define cs_log(fmt, params...)							cs_log_txt(fmt, ##params)
#define cs_log_dump(buf, n, fmt, params...)				cs_log_hex(buf,  n, fmt, ##params)
#define cs_log_dbg(a, fmt, params...)					do { if (debuglog) cs_log_txt(fmt, ##params); } while(0)
#define cs_log_dump_dbg(buf, n, fmt, params...)			do { if (debuglog) cs_log_hex(buf , n, fmt, ##params); } while(0)
	
void cs_log_txt(const char* format, ... ) {

	FILE *fp = NULL;
	if(1) { fp = fopen("log.txt", "a"); }

	va_list ap, ap2;
	va_start(ap, format);
	va_copy(ap2, ap);

	if(1) { vfprintf(stderr, format, ap); }
	va_end(ap);

	if(fp) { vfprintf(fp, format, ap2); }
	va_end(ap2);

	if(1) { fprintf(stderr, "\n"); }
	if(fp)  { fprintf(fp, "\n"); fclose(fp); }
}

char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len)
{
	int32_t i = 0;
	target[0] = '\0';
	m = m ? 3 : 2;
	if(m * n >= len)
	{ n = (len / m) - 1; }
	while(i < n)
	{
		snprintf(target + (m * i), len - (m * i), "%02X%s", *buf++, m > 2 ? " " : "");
		i++;
	}
	return target;
}

void cs_log_hex(const uint8_t *buf, int32_t n, const char *fmt, ...)
{
	FILE *fp = NULL;
	char log_txt[512];
	const char *newline;
	int32_t i;

	newline = "\n";
	
	va_list ap;
	va_start(ap, fmt);
	vsnprintf(log_txt, sizeof(log_txt), fmt, ap);
	va_end(ap);

	if(1) { fp = fopen("log.txt", "a"); }

	if(1)
	{
		fwrite(log_txt, sizeof(char), strlen(log_txt), stderr);
		fwrite(newline, sizeof(char), strlen(newline), stderr);
	}
	if(fp)
	{
		fwrite(log_txt, sizeof(char), strlen(log_txt), fp);
		fwrite(newline, sizeof(char), strlen(newline), fp);
	}

	if(buf)
	{
		for(i = 0; i < n; i += 16)
		{
			cs_hexdump(1, buf + i, (n - i > 16) ? 16 : n - i, log_txt, sizeof(log_txt));
			if(1)
			{
				fwrite(log_txt, sizeof(char), strlen(log_txt), stderr);
				fwrite(newline, sizeof(char), strlen(newline), stderr);
			}
			if(fp) {
				fwrite(log_txt, sizeof(char), strlen(log_txt), fp);
				fwrite(newline, sizeof(char), strlen(newline), fp);
			}
		}
	}

	if(fp) { fclose(fp); }
}
	

static void ParseTSPackets(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize)
{
	int32_t i, j, k;
	uint32_t tsHeader;
	uint16_t pid, offset;
	uint8_t scramblingControl, payloadStart, oddeven;
	int8_t oddKeyUsed;
	uint32_t *deskey;
	uint8_t *pdata;
	uint8_t *packetClusterA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS][64];  //separate cluster arrays for video and each audio track
	uint8_t *packetClusterV[256];
	void *csakeyA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0};
	void *csakeyV = 0;
	emu_stream_client_key_data *keydata;
	uint32_t scrambled_packets = 0;
	uint32_t scrambled_packetsA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS]  = {0};
	packetClusterV[0] = NULL;
	uint32_t cs =0;  //video cluster start
	uint32_t ce =1;  //video cluster end
	uint32_t csa[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0};  //cluster index for audio tracks

	for(i=0; i<bufLength; i+=packetSize)
	{		
		tsHeader = b2i(4, stream_buf+i);
		pid = (tsHeader & 0x1fff00) **** 8;
		scramblingControl = tsHeader & 0xc0;
		payloadStart = (tsHeader & 0x400000) **** 22;

		if(tsHeader & 0x20)
			{ offset = 4 + stream_buf[i+4] + 1; }
		else
			{ offset = 4; }
		
		if(packetSize-offset < 1)
			{ continue; }
		
		if(data->have_pat_data != 1)
		{					
			if(pid == 0)
			{ 
				ParseTSData(0x00, 0xFF, 16, &data->have_pat_data, data->data, sizeof(data->data), &data->data_pos, payloadStart, 
								stream_buf+i+offset, packetSize-offset, ParsePATData, data);
			}
		
			continue;
		}

		if(!data->pmt_pid)
		{
			data->have_pat_data = 0;
			continue;
		}

		if(data->have_pmt_data != 1)
		{
			if(pid == data->pmt_pid)
			{
				ParseTSData(0x02, 0xFF, 21, &data->have_pmt_data, data->data, sizeof(data->data), &data->data_pos, payloadStart, 
								stream_buf+i+offset, packetSize-offset, ParsePMTData, data);
			}
		
			continue;
		}

		if(data->ecm_pid && pid == data->ecm_pid)
		{			
			ParseTSData(0x80, 0xFE, 10, &data->have_ecm_data, data->data, sizeof(data->data), &data->data_pos, payloadStart, 
							stream_buf+i+offset, packetSize-offset, ParseECMData, data);
			continue;
		}
		
		//if(pid==0x1026)
		//cs_log_dump(stream_buf+i+offset, packetSize-offset, "control packet 1");

		//if(pid==0x1028)
		//cs_log_dump(stream_buf+i+offset, packetSize-offset, "control packet 2");
		
		if(scramblingControl == 0)
			{ continue; }
		
		if(!(stream_buf[i+3] & 0x10))
		{
			stream_buf[i+3] &= 0x3F;
			
			cs_log_dump(stream_buf+i, packetSize, "111");
			continue;
		}

		oddKeyUsed = scramblingControl == 0xC0 ? 1 : 0;

		keydata = &data->key;
	
		int8_t can_decode = 0;
		
		if(pid == data->video_pid) 
		{
			can_decode = 1;
		}
		else
		{
			for(j=0; j<data->audio_pid_count; j++)
			{
					if(pid == data->audio_pids[j])
					{
						can_decode = 1;
						break;
					}
			}
		}		
		
		if(can_decode) 
		{
			static uint8_t dyn_key[184];
			
			int8_t matches00 = 0;
			int8_t matchesFF = 0;
			int8_t last00_was_good = 0;
			int8_t lastFF_was_good = 0;
			
			int8_t limit = 64;
			
			for(j=0; j< 184; j++)
			{
				if(stream_buf[i+4+j] == 0x00)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x00;
				}
				else if(stream_buf[i+4+j] == 0x3F)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x3F;
				}
				else
				{
					if(last00_was_good) matches00--;
					else matches00 -= 2;
						
					if(matches00 < 0) matches00 = 0;
					last00_was_good = 0;
				}
							
				if(stream_buf[i+4+j] == 0xC0)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x3F;
				}
				else if(stream_buf[i+4+j] == 0xFF)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x00;
				}
				else
				{
					if(lastFF_was_good) matchesFF--;
					else matchesFF -= 2;
						
					if(matchesFF < 0) matchesFF = 0;
					lastFF_was_good = 0;
				}
			}
			
			for(j=183; j >= 0; j--)
			{
				if(stream_buf[i+4+j] == 0x00)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x00;
				}
				else if(stream_buf[i+4+j] == 0x3F)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x3F;
				}
				else
				{
					if(last00_was_good) matches00--;
					else matches00 -= 2;
						
					if(matches00 < 0) matches00 = 0;
					last00_was_good = 0;
				}
							
				if(stream_buf[i+4+j] == 0xC0)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x3F;
				}
				else if(stream_buf[i+4+j] == 0xFF)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x00;
				}
				else
				{
					if(lastFF_was_good) matchesFF--;
					else matchesFF -= 2;
						
					if(matchesFF < 0) matchesFF = 0;
					lastFF_was_good = 0;
				}
			}
			

			for(j=0; j<184; j++)
			{
				stream_buf[i+4+j] ^= dyn_key[j];
			}
			
			stream_buf[i+3] &= 0x3F;
			
		}

	}
}


int main(int argc, char *argv[])
{
#define EMU_DVB_MAX_TS_PACKETS 278
#define EMU_DVB_BUFFER_SIZE_CSA 188*EMU_DVB_MAX_TS_PACKETS
#define EMU_DVB_BUFFER_WAIT_CSA 188*(EMU_DVB_MAX_TS_PACKETS-128) 
#define EMU_DVB_BUFFER_SIZE EMU_DVB_BUFFER_SIZE_CSA

	FILE* streamfd;
	FILE* writefd;
	int32_t streamStatus;
	uint8_t *stream_buf;
	uint16_t packetCount = 0, packetSize = 0, startOffset = 0;
	uint32_t remainingDataPos, remainingDataLength;
	int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait;
	int32_t bytesRead = 0;
	emu_stream_client_data *data;

	if(argc < 4) 
	{
		printf("usage: %s <input file> <service id> <output file>\n", argv[0]);
		return 0;
	}

	stream_buf = (uint8_t*)malloc(EMU_DVB_BUFFER_SIZE);
	if(!stream_buf)
	{
		return 0;
	}
	
	data = (emu_stream_client_data*)malloc(sizeof(emu_stream_client_data));
	if(!data)
	{
		return 0;
	}
	
	data->srvid = atoi(argv[2]);
	
	streamfd = fopen(argv[1], "rb");
	if(!streamfd)
	{
		return 0;
	}

	writefd = fopen(argv[3], "wb");
	if(!writefd)
	{
		return 0;
	}
	
	while(1)
	{
		cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_CSA;
		cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_CSA;
		
		streamStatus = fread(stream_buf+bytesRead, 1, cur_dvb_buffer_size-bytesRead, streamfd);
		if(ferror(streamfd) || feof(streamfd))
		{
			break;
		}
		
		bytesRead += streamStatus;
		
		if(bytesRead >= cur_dvb_buffer_wait)
		{	
			startOffset = 0;
			if(stream_buf[0] != 0x47 || packetSize == 0)  // only search if not starting on ts packet or unknown packet size 
			{
				SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset);
			}
			if(packetSize == 0)
			{
				bytesRead = 0;
			}
			else
			{			
				packetCount = ((bytesRead-startOffset) / packetSize);
				
				ParseTSPackets(data, stream_buf+startOffset, packetCount*packetSize, packetSize);

				for(int32_t i=0; i<packetCount; i++)
				{		
					int32_t tsHeader = b2i(4, stream_buf+startOffset+(i*packetSize));
					int16_t pid = (tsHeader & 0x1fff00) **** 8;
					
					int8_t belongs_to_service = 0;
						
					if(pid == data->video_pid) 
					{
						belongs_to_service = 1;
					}
					else if(pid == data->pmt_pid) 
					{
						belongs_to_service = 1;
					}
					else
					{
						for(int32_t j=0; j<data->audio_pid_count; j++)
						{
								if(pid == data->audio_pids[j])
								{
									belongs_to_service = 1;
									break;
								}
						}
					}	
					
					if(pid == 0 || belongs_to_service)
					{		
						fwrite(stream_buf+startOffset+(i*packetSize), 1, packetSize, writefd);
					}
				}
					 
				remainingDataPos = startOffset+(packetCount*packetSize);
				remainingDataLength = bytesRead-remainingDataPos;
				
				if(remainingDataPos < remainingDataLength)
					{ memmove(stream_buf, stream_buf+remainingDataPos, remainingDataLength); }
				else
					{ memcpy(stream_buf, stream_buf+remainingDataPos, remainingDataLength); }
				
				bytesRead = remainingDataLength;
			}
		}
			
	}
	
	fclose(writefd);
	fclose(streamfd);
	free(data);
	free(stream_buf);
	return 0;
}

usage: poc <input file> <service id> <output file>
 
Last edited:

K2TSET

Registered
Messages
125
Some quick search on Wegener and Compel

Wegener Compel / MediaPlan

Compel Control is a network management system that provides comprehensive control of satellite-based broadcast networks for video, audio and data. Compel provides reliable control of broadcast networks in real-time and of the connected devices like Unity IRD or iPump Server).

Could be the CA described here https://docs.google.com/viewer?url=patentimages.storage.googleapis.com/pdfs/USRE41919.pdf
 

kebien

Registered
Messages
1,329


some poc code: (not perfect, just a proof of concept for those that want to do the challenge and are stuck):

Code:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE //needed for PTHREAD_MUTEX_RECURSIVE on some plattforms and maybe other things; do not remove
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

#define SCT_LEN(sct) (3+((sct[1]&0x0f)<<8)+sct[2])

#define EMU_STREAM_SERVER_MAX_CONNECTIONS 8
#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 16

	typedef struct 
	{
		uint32_t pvu_des_ks[8][2][32];
		int8_t pvu_csa_used;
		void* pvu_csa_ks[8];
	} emu_stream_client_key_data;

	typedef struct 
	{
		int8_t have_pat_data;
		int8_t have_pmt_data;
		int8_t have_ecm_data;
		uint8_t data[1024+208];
		uint16_t data_pos;
		uint16_t srvid;
		uint16_t pmt_pid;
		uint16_t ecm_pid;
		uint16_t video_pid;
		uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS];
		uint8_t audio_pid_count;
		int16_t ecm_nb;
		emu_stream_client_key_data key;
	} emu_stream_client_data;


uint32_t b2i(int32_t n, const uint8_t *b)
{
	switch(n) {
	case 2:
		return  (b[0] <<  8) |  b[1];
	case 3:
		return  (b[0] << 16) | (b[1] <<  8) |  b[2];
	case 4:
		return ((b[0] << 24) | (b[1] << 16) | (b[2] <<8 ) | b[3]) & 0xffffffff;
	default:
		return 0;
	}
	return 0;
}

uint8_t *i2b_buf(int32_t n, uint32_t i, uint8_t *b)
{
	switch(n) {
	case 2:
		b[0] = (i**** 8) & 0xff;
		b[1] = (i    ) & 0xff;
		break;
	case 3:
		b[0] = (i****16) & 0xff;
		b[1] = (i**** 8) & 0xff;
		b[2] = (i    ) & 0xff;
	case 4:
		b[0] = (i****24) & 0xff;
		b[1] = (i****16) & 0xff;
		b[2] = (i**** 8) & 0xff;
		b[3] = (i    ) & 0xff;
		break;
	}
	return b;
}

static void SearchTsPackets(uint8_t *buf, uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset)
{
	uint32_t i;
	
	(*packetSize) = 0;
	(*startOffset) = 0;

	for(i=0; i<bufLength; i++) {	
		if(buf[i] == 0x47) {
			if((buf[i+188] == 0x47) & (buf[i+376] == 0x47)) {  //if three packets align, probably safe to assume correct size.
				(*packetSize) = 188;
				(*startOffset) = i;
				return;
			}
			else if((buf[i+204] == 0x47) & (buf[i+408] == 0x47)) {
				(*packetSize) = 204;
				(*startOffset) = i;
				return;
			}
			else if((buf[i+208] == 0x47) & (buf[i+416] == 0x47)) {
				(*packetSize) = 208;
				(*startOffset) = i;
				return;
			}					
		}	
	}
}

typedef void (*ts_data_callback)(emu_stream_client_data *cdata);

static void ParseTSData(uint8_t table_id, uint8_t table_mask, uint8_t min_table_length, int8_t* flag, uint8_t* data,
							uint16_t data_length, uint16_t* data_pos, int8_t payloadStart, uint8_t* buf, int32_t len,
							ts_data_callback func, emu_stream_client_data *cdata)
{
	uint16_t section_length;
	int32_t i;
	int8_t found_start = 0;
	uint16_t offset = 0;
	int32_t free_data_length;
	int32_t copySize;
	
	if(len < 1)
		{ return; }
	
	if(*flag == 0 && !payloadStart)
		{ return; }

	if(*flag == 0)
	{ 
		*data_pos = 0;
		 offset = 1 + buf[0];
	}
	else if(payloadStart)
	{ 
		offset = 1; 
	}
	
	if(len-offset < 1)
		{ return; }
	
	free_data_length = data_length - *data_pos;
	copySize = (len-offset) > free_data_length ? free_data_length : (len-offset);
	
	memcpy(data+(*data_pos), buf+offset, copySize);	
	(*data_pos) += copySize;

	found_start = 0;
	for(i=0; i < *data_pos; i++)
	{
		if((data[i] & table_mask) == table_id)
		{
			if(i != 0)
			{
				if((*data_pos)-i > i)
					{ memmove(data, &data[i], (*data_pos)-i); }
				else
					{ memcpy(data, &data[i], (*data_pos)-i); }
			
				*data_pos -= i;
			}
			found_start = 1;
			break;
		}	
	}
	if(!found_start)
		{ *flag = 0; return; }

	*flag = 2;

	if(*data_pos < 3)
		{ return; }

	section_length = SCT_LEN(data);

	if(section_length > data_length || section_length < min_table_length)
		{ *flag = 0; return; }
	
	if((*data_pos) < section_length)
		{ return; }

	func(cdata);
	
	found_start = 0;
	for(i=section_length; i < *data_pos; i++)
	{
		if((data[i] & table_mask) == table_id)
		{
			if((*data_pos)-i > i)
				{ memmove(data, &data[i], (*data_pos)-i); }
			else
				{ memcpy(data, &data[i], (*data_pos)-i); }
			
			*data_pos -= i;
			found_start = 1;
			break;
		}	
	}	
	if(!found_start)
		{ *data_pos = 0; }
	
	*flag = 1;
}

static void ParsePATData(emu_stream_client_data *cdata)
{
	uint8_t* data = cdata->data;
	uint16_t section_length = SCT_LEN(data);
	uint16_t srvid;
	int32_t i;

	for(i=8; i+7<section_length; i+=4)
	{
		srvid = b2i(2, data+i);
		
		if(srvid == 0)
			{ continue; }
		
		if(cdata->srvid == srvid)
		{
			cdata->pmt_pid = b2i(2, data+i+2) & 0x1FFF;
			printf("[Emu] stream found pmt pid: %X\n", cdata->pmt_pid);
			break;
		}
	}
}

static void ParsePMTData(emu_stream_client_data *cdata)
{
	uint8_t* data = cdata->data;
	
	uint16_t section_length = SCT_LEN(data);
	int32_t i;
	uint16_t program_info_length = 0, es_info_length = 0;
	uint8_t descriptor_tag = 0, descriptor_length = 0;
	uint8_t stream_type;
	uint16_t stream_pid, caid;
	
	program_info_length = b2i(2, data+10) &0xFFF;
	
	if(12+program_info_length >= section_length)
		{ return; }
	
	for(i=12; i+1 < 12+program_info_length; i+=descriptor_length+2)
	{
		descriptor_tag = data[i];
		descriptor_length = data[i+1];
		
		if(descriptor_length < 1)
			{ break; }
			
		if(i+1+descriptor_length >= 12+program_info_length)
			{ break; }
		
		if(descriptor_tag == 0x09 && descriptor_length >= 4)
		{
			caid = b2i(2, data+i+2);
			
			if(caid****8 == 0x0E)
			{
		    	cdata->ecm_pid = b2i(2, data+i+4) &0x1FFF;
		    	printf("[Emu] stream found ecm_pid: %X\n", cdata->ecm_pid);
		    	break;
		    }
		}
	}
		
	for(i=12+program_info_length; i+4<section_length; i+=5+es_info_length)
	{
		stream_type = data[i];
		stream_pid = b2i(2, data+i+1) &0x1FFF;
		es_info_length = b2i(2, data+i+3) &0xFFF;
		
		if(stream_type == 0x01 || stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B 
			|| stream_type == 0x24 || stream_type == 0x42 || stream_type == 0x80 || stream_type == 0xD1 
			|| stream_type == 0xEA)
		{ 
			cdata->video_pid = stream_pid;
			printf("[Emu] stream found video pid: %X\n", stream_pid);
		}
		
		else if(stream_type == 0x03 || stream_type == 0x04 || stream_type == 0x0F || stream_type == 0x11 
			|| stream_type == 0x81 || stream_type == 0x87)
		{
			if(cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS)
				{ continue; }
			
			cdata->audio_pids[cdata->audio_pid_count] = stream_pid;
			cdata->audio_pid_count++;
			printf("[Emu] stream found audio pid: %X\n", stream_pid);
		}
	}
}

static void ParseECMData(emu_stream_client_data *cdata)
{
	uint8_t* data = cdata->data;
	uint16_t section_length = SCT_LEN(data);
	uint8_t dcw[16];
	
	if(section_length < 0xb)
		{ return; }

	if(data[0xb] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[0xb] == 0)
		|| ((cdata->ecm_nb - data[0xb]) > 5))
	{
		cdata->ecm_nb = data[0xb];

		//ParseECM(data, dcw, &cdata->key);
	}
}

void cs_log_txt(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len);
void cs_log_hex(const uint8_t *buf, int32_t n, const char *fmt, ...) __attribute__((format(printf, 3, 4)));

#define cs_log(fmt, params...)							cs_log_txt(fmt, ##params)
#define cs_log_dump(buf, n, fmt, params...)				cs_log_hex(buf,  n, fmt, ##params)
#define cs_log_dbg(a, fmt, params...)					do { if (debuglog) cs_log_txt(fmt, ##params); } while(0)
#define cs_log_dump_dbg(buf, n, fmt, params...)			do { if (debuglog) cs_log_hex(buf , n, fmt, ##params); } while(0)
	
void cs_log_txt(const char* format, ... ) {

	FILE *fp = NULL;
	if(1) { fp = fopen("log.txt", "a"); }

	va_list ap, ap2;
	va_start(ap, format);
	va_copy(ap2, ap);

	if(1) { vfprintf(stderr, format, ap); }
	va_end(ap);

	if(fp) { vfprintf(fp, format, ap2); }
	va_end(ap2);

	if(1) { fprintf(stderr, "\n"); }
	if(fp)  { fprintf(fp, "\n"); fclose(fp); }
}

char *cs_hexdump(int32_t m, const uint8_t *buf, int32_t n, char *target, int32_t len)
{
	int32_t i = 0;
	target[0] = '\0';
	m = m ? 3 : 2;
	if(m * n >= len)
	{ n = (len / m) - 1; }
	while(i < n)
	{
		snprintf(target + (m * i), len - (m * i), "%02X%s", *buf++, m > 2 ? " " : "");
		i++;
	}
	return target;
}

void cs_log_hex(const uint8_t *buf, int32_t n, const char *fmt, ...)
{
	FILE *fp = NULL;
	char log_txt[512];
	const char *newline;
	int32_t i;

	newline = "\n";
	
	va_list ap;
	va_start(ap, fmt);
	vsnprintf(log_txt, sizeof(log_txt), fmt, ap);
	va_end(ap);

	if(1) { fp = fopen("log.txt", "a"); }

	if(1)
	{
		fwrite(log_txt, sizeof(char), strlen(log_txt), stderr);
		fwrite(newline, sizeof(char), strlen(newline), stderr);
	}
	if(fp)
	{
		fwrite(log_txt, sizeof(char), strlen(log_txt), fp);
		fwrite(newline, sizeof(char), strlen(newline), fp);
	}

	if(buf)
	{
		for(i = 0; i < n; i += 16)
		{
			cs_hexdump(1, buf + i, (n - i > 16) ? 16 : n - i, log_txt, sizeof(log_txt));
			if(1)
			{
				fwrite(log_txt, sizeof(char), strlen(log_txt), stderr);
				fwrite(newline, sizeof(char), strlen(newline), stderr);
			}
			if(fp) {
				fwrite(log_txt, sizeof(char), strlen(log_txt), fp);
				fwrite(newline, sizeof(char), strlen(newline), fp);
			}
		}
	}

	if(fp) { fclose(fp); }
}
	

static void ParseTSPackets(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize)
{
	int32_t i, j, k;
	uint32_t tsHeader;
	uint16_t pid, offset;
	uint8_t scramblingControl, payloadStart, oddeven;
	int8_t oddKeyUsed;
	uint32_t *deskey;
	uint8_t *pdata;
	uint8_t *packetClusterA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS][64];  //separate cluster arrays for video and each audio track
	uint8_t *packetClusterV[256];
	void *csakeyA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0};
	void *csakeyV = 0;
	emu_stream_client_key_data *keydata;
	uint32_t scrambled_packets = 0;
	uint32_t scrambled_packetsA[EMU_STREAM_MAX_AUDIO_SUB_TRACKS]  = {0};
	packetClusterV[0] = NULL;
	uint32_t cs =0;  //video cluster start
	uint32_t ce =1;  //video cluster end
	uint32_t csa[EMU_STREAM_MAX_AUDIO_SUB_TRACKS] = {0};  //cluster index for audio tracks

	for(i=0; i<bufLength; i+=packetSize)
	{		
		tsHeader = b2i(4, stream_buf+i);
		pid = (tsHeader & 0x1fff00) **** 8;
		scramblingControl = tsHeader & 0xc0;
		payloadStart = (tsHeader & 0x400000) **** 22;

		if(tsHeader & 0x20)
			{ offset = 4 + stream_buf[i+4] + 1; }
		else
			{ offset = 4; }
		
		if(packetSize-offset < 1)
			{ continue; }
		
		if(data->have_pat_data != 1)
		{					
			if(pid == 0)
			{ 
				ParseTSData(0x00, 0xFF, 16, &data->have_pat_data, data->data, sizeof(data->data), &data->data_pos, payloadStart, 
								stream_buf+i+offset, packetSize-offset, ParsePATData, data);
			}
		
			continue;
		}

		if(!data->pmt_pid)
		{
			data->have_pat_data = 0;
			continue;
		}

		if(data->have_pmt_data != 1)
		{
			if(pid == data->pmt_pid)
			{
				ParseTSData(0x02, 0xFF, 21, &data->have_pmt_data, data->data, sizeof(data->data), &data->data_pos, payloadStart, 
								stream_buf+i+offset, packetSize-offset, ParsePMTData, data);
			}
		
			continue;
		}

		if(data->ecm_pid && pid == data->ecm_pid)
		{			
			ParseTSData(0x80, 0xFE, 10, &data->have_ecm_data, data->data, sizeof(data->data), &data->data_pos, payloadStart, 
							stream_buf+i+offset, packetSize-offset, ParseECMData, data);
			continue;
		}
		
		//if(pid==0x1026)
		//cs_log_dump(stream_buf+i+offset, packetSize-offset, "control packet 1");

		//if(pid==0x1028)
		//cs_log_dump(stream_buf+i+offset, packetSize-offset, "control packet 2");
		
		if(scramblingControl == 0)
			{ continue; }
		
		if(!(stream_buf[i+3] & 0x10))
		{
			stream_buf[i+3] &= 0x3F;
			
			cs_log_dump(stream_buf+i, packetSize, "111");
			continue;
		}

		oddKeyUsed = scramblingControl == 0xC0 ? 1 : 0;

		keydata = &data->key;
	
		int8_t can_decode = 0;
		
		if(pid == data->video_pid) 
		{
			can_decode = 1;
		}
		else
		{
			for(j=0; j<data->audio_pid_count; j++)
			{
					if(pid == data->audio_pids[j])
					{
						can_decode = 1;
						break;
					}
			}
		}		
		
		if(can_decode) 
		{
			static uint8_t dyn_key[184];
			
			int8_t matches00 = 0;
			int8_t matchesFF = 0;
			int8_t last00_was_good = 0;
			int8_t lastFF_was_good = 0;
			
			int8_t limit = 64;
			
			for(j=0; j< 184; j++)
			{
				if(stream_buf[i+4+j] == 0x00)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x00;
				}
				else if(stream_buf[i+4+j] == 0x3F)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x3F;
				}
				else
				{
					if(last00_was_good) matches00--;
					else matches00 -= 2;
						
					if(matches00 < 0) matches00 = 0;
					last00_was_good = 0;
				}
							
				if(stream_buf[i+4+j] == 0xC0)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x3F;
				}
				else if(stream_buf[i+4+j] == 0xFF)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x00;
				}
				else
				{
					if(lastFF_was_good) matchesFF--;
					else matchesFF -= 2;
						
					if(matchesFF < 0) matchesFF = 0;
					lastFF_was_good = 0;
				}
			}
			
			for(j=183; j >= 0; j--)
			{
				if(stream_buf[i+4+j] == 0x00)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x00;
				}
				else if(stream_buf[i+4+j] == 0x3F)
				{
					last00_was_good = 1;
					matches00++;
					if(matches00 > limit) dyn_key[j] = 0x3F;
				}
				else
				{
					if(last00_was_good) matches00--;
					else matches00 -= 2;
						
					if(matches00 < 0) matches00 = 0;
					last00_was_good = 0;
				}
							
				if(stream_buf[i+4+j] == 0xC0)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x3F;
				}
				else if(stream_buf[i+4+j] == 0xFF)
				{
					lastFF_was_good = 1;
					matchesFF++;
					if(matchesFF > limit) dyn_key[j] = 0x00;
				}
				else
				{
					if(lastFF_was_good) matchesFF--;
					else matchesFF -= 2;
						
					if(matchesFF < 0) matchesFF = 0;
					lastFF_was_good = 0;
				}
			}
			

			for(j=0; j<184; j++)
			{
				stream_buf[i+4+j] ^= dyn_key[j];
			}
			
			stream_buf[i+3] &= 0x3F;
			
		}

	}
}


int main(int argc, char *argv[])
{
#define EMU_DVB_MAX_TS_PACKETS 278
#define EMU_DVB_BUFFER_SIZE_CSA 188*EMU_DVB_MAX_TS_PACKETS
#define EMU_DVB_BUFFER_WAIT_CSA 188*(EMU_DVB_MAX_TS_PACKETS-128) 
#define EMU_DVB_BUFFER_SIZE EMU_DVB_BUFFER_SIZE_CSA

	FILE* streamfd;
	FILE* writefd;
	int32_t streamStatus;
	uint8_t *stream_buf;
	uint16_t packetCount = 0, packetSize = 0, startOffset = 0;
	uint32_t remainingDataPos, remainingDataLength;
	int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait;
	int32_t bytesRead = 0;
	emu_stream_client_data *data;

	if(argc < 4) 
	{
		printf("usage: %s <input file> <service id> <output file>\n", argv[0]);
		return 0;
	}

	stream_buf = (uint8_t*)malloc(EMU_DVB_BUFFER_SIZE);
	if(!stream_buf)
	{
		return 0;
	}
	
	data = (emu_stream_client_data*)malloc(sizeof(emu_stream_client_data));
	if(!data)
	{
		return 0;
	}
	
	data->srvid = atoi(argv[2]);
	
	streamfd = fopen(argv[1], "rb");
	if(!streamfd)
	{
		return 0;
	}

	writefd = fopen(argv[3], "wb");
	if(!writefd)
	{
		return 0;
	}
	
	while(1)
	{
		cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_CSA;
		cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_CSA;
		
		streamStatus = fread(stream_buf+bytesRead, 1, cur_dvb_buffer_size-bytesRead, streamfd);
		if(ferror(streamfd) || feof(streamfd))
		{
			break;
		}
		
		bytesRead += streamStatus;
		
		if(bytesRead >= cur_dvb_buffer_wait)
		{	
			startOffset = 0;
			if(stream_buf[0] != 0x47 || packetSize == 0)  // only search if not starting on ts packet or unknown packet size 
			{
				SearchTsPackets(stream_buf, bytesRead, &packetSize, &startOffset);
			}
			if(packetSize == 0)
			{
				bytesRead = 0;
			}
			else
			{			
				packetCount = ((bytesRead-startOffset) / packetSize);
				
				ParseTSPackets(data, stream_buf+startOffset, packetCount*packetSize, packetSize);

				for(int32_t i=0; i<packetCount; i++)
				{		
					int32_t tsHeader = b2i(4, stream_buf+startOffset+(i*packetSize));
					int16_t pid = (tsHeader & 0x1fff00) **** 8;
					
					int8_t belongs_to_service = 0;
						
					if(pid == data->video_pid) 
					{
						belongs_to_service = 1;
					}
					else if(pid == data->pmt_pid) 
					{
						belongs_to_service = 1;
					}
					else
					{
						for(int32_t j=0; j<data->audio_pid_count; j++)
						{
								if(pid == data->audio_pids[j])
								{
									belongs_to_service = 1;
									break;
								}
						}
					}	
					
					if(pid == 0 || belongs_to_service)
					{		
						fwrite(stream_buf+startOffset+(i*packetSize), 1, packetSize, writefd);
					}
				}
					 
				remainingDataPos = startOffset+(packetCount*packetSize);
				remainingDataLength = bytesRead-remainingDataPos;
				
				if(remainingDataPos < remainingDataLength)
					{ memmove(stream_buf, stream_buf+remainingDataPos, remainingDataLength); }
				else
					{ memcpy(stream_buf, stream_buf+remainingDataPos, remainingDataLength); }
				
				bytesRead = remainingDataLength;
			}
		}
			
	}
	
	fclose(writefd);
	fclose(streamfd);
	free(data);
	free(stream_buf);
	return 0;
}

usage: poc <input file> <service id> <output file>
And act of kindness and compile this poc for windows?
I think I have some hannels i can see using compel.

One question,and excuse me i have not looked at the TS file provided : are all packets encrypted,including headers? I have seem other services that even normal tables (PAT,PMT) are encrypted,you can;t even get to scan this channels since tables are not readable.
I assume those are using a similar approach than compel,or are actually also compel.
 

okidokios

Registered
Messages
54
And act of kindness and compile this poc for windows?
I think I have some hannels i can see using compel.

One question,and excuse me i have not looked at the TS file provided : are all packets encrypted,including headers? I have seem other services that even normal tables (PAT,PMT) are encrypted,you can;t even get to scan this channels since tables are not readable.
I assume those are using a similar approach than compel,or are actually also compel.

+1 How to comiple in windows please?
 
Top