31.01.2009, 12:54
|
#1
|
[Клиент-Сервер] описание NewCamD 5.25
Писано сие творение aiZent-ом, в случае копирования, вы обязаны установить ссылку на эту тему.
Протокол NewCamd использует для транспорта данных сетевой протокол TCP, данные соединения шифруются по алгоритму тройной DES, длина ключа 14 байт (112 бит)
После установления подключения к серверу, клиент получает от сервера 14 случайных байт, эти байты проXORены 3DES ключем из конфиг-файла
Клиент <- Сервер 1/5
шифрование: нет
----------------------------------------------------------
00: 77 9d cc 5d d2 0d 59 2e dc ed b8 17 c1 ab w ] Y.
14 рандомных байт используются для передачи шифрованых логина и пароля.
Все дальнейшие пакеты идут с "префиксом" в 2 байта, в которых указан код команды. Коды команд:
Код:
#define CWS_FIRSTCMDNO 0xe0
typedef enum
{
MSG_CLIENT_2_SERVER_LOGIN = CWS_FIRSTCMDNO,
MSG_CLIENT_2_SERVER_LOGIN_ACK,
MSG_CLIENT_2_SERVER_LOGIN_NAK,
MSG_CARD_DATA_REQ,
MSG_CARD_DATA,
MSG_SERVER_2_CLIENT_NAME,
MSG_SERVER_2_CLIENT_NAME_ACK,
MSG_SERVER_2_CLIENT_NAME_NAK,
MSG_SERVER_2_CLIENT_LOGIN,
MSG_SERVER_2_CLIENT_LOGIN_ACK,
MSG_SERVER_2_CLIENT_LOGIN_NAK,
MSG_ADMIN,
MSG_ADMIN_ACK,
MSG_ADMIN_LOGIN,
MSG_ADMIN_LOGIN_ACK,
MSG_ADMIN_LOGIN_NAK,
MSG_ADMIN_COMMAND,
MSG_ADMIN_COMMAND_ACK,
MSG_ADMIN_COMMAND_NAK,
MSG_KEEPALIVE = CWS_FIRSTCMDNO + 0x1d
} net_msg_type_t;
Клиент отсылает пакет с кодом команды MSG_CLIENT_2_SERVER_LOGIN, который содержит имя пользователя и пароль. Имя пользователя должно заканчиваться символом 0x00 (C-String), пароль следует сразу за логином. Пароль шифруется через функцию crypt GLIBC с салтом "$1$abcdefgh$" и также заканчивается 0x00.
Клиент -> Сервер 1/5
шифрование: да, 14 байтами из конфиг файла
----------------------------------------------------------
00: e0 00 29 64 75 6d 6d 79 00 24 31 24 61 62 63 64 )dummy $1$abcd
10: 65 66 67 68 24 6e 70 53 45 54 51 73 72 49 6d 33 efgh$npSETQsrIm3
20: 35 4d 51 66 69 55 49 41 64 6e 2e 00 5MQfiUIAdn.
Шифрование:
char *userpassword="password";
char *passwdcrypt;
passwdcrypt = (uint8*)__md5_crypt(userpassword, "$1$abcdefgh$");
таким вот образом выполняется аналог шифрования md5_crypt GLIB
(когда я реализовывал поддержку newcamd в Borland C++ Builder 6, пришлось соорудить свой аналог md5_crypt из нескольких файлов (под Win функций GlibC увы нет  ) кому нужно забираем вложенный файл).
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
Хостер
Регистрация: 24.12.2006
Адрес: Кост.область
Сообщений: 2,193
 : 289
 : 297 (2)
|
|
aiZent на форуме
|
31.01.2009, 13:32
|
#2
|
Хостер (2,193)
Репа: 289
Регистрация: 24.12.2006
Адрес: Кост.область
 : 289
 : 297 (2)
|
Сервер проверяет логин/пароль, если они верные, то отсылает клиенту пустой пакет с кодом MSG_CLIENT_2_SERVER_LOGIN_ACK, если неверные, то MSG_CLIENT_2_SERVER_LOGIN_NAK. Хотелось бы отметить, если сервер закрывает соединение, ваш DES ключ (14 байт из конфига) неверный, т.к. коды команды с неверным ключем будет испорчены и сервер просто непоймет, что от него хотят 
После выполнения авторизации меняется ключ шифрования соединения, 14 байт XORятся шифрованным паролем, назовем его ключ сессии.
Код:
for (i = 0; i < strlen(cryptPw); i++) deskey[i] ^= cryptPw[i];
Клиент <- Сервер 1/5
шифрование: да, 14 байт
----------------------------------------------------------
00: e1 00 00
Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
aiZent на форуме
|
|
31.01.2009, 13:41
|
#3
|
Хостер (2,193)
Репа: 289
Регистрация: 24.12.2006
Адрес: Кост.область
 : 289
 : 297 (2)
|
Клиент отсылает серверу пустой пакет с кодом MSG_CARD_DATA_REQ
Клиент -> Сервер 1/5
шифрование: ключ сессии
----------------------------------------------------------
00: e3 00 00
Сервер пересылает на клиент пакет с кодом MSG_CARD_DATA и данными доступных карт
Клиент <- Сервер 1/5
шифрование: ключ сессии
----------------------------------------------------------
00: e4 00 17 01 09 0f 00 00 00 00 XX XX XX XX 01 00 a
10: 00 00 00 00 00 00 XX XX XX 00
байт 1: MSG_CARD_DATA
байт 2/3: длина данных
байт 4: идентификатор пользователя
байт 5/6: CAID
байты 7-14: номер карточки (только для идентификатора пользователя == 1, иначе заполнено 0x00)
байт 15: число провайдеров на карте (nProv)
Код:
for (i = 0; i < nProv; i++)
{
byte 16+11*i - 18+11*i: IDENT
byte 19+11*i - 26+11*i: идентификатор провайдера (только для идентификатора пользователя == 1, иначе заполнено 0x00)
}
* небольшое пояснение, мой вариант сервера всегда отправляет
mbuf[3] = item+10; (item порядковый номер клиента)
....
mbuf[6] = 0x00;
mbuf[7] = 0x00;
mbuf[8] = 0x00;
mbuf[9] = 0x00;
mbuf[10] = 0x00;
mbuf[11] = 0x00;
mbuf[12] = 0x00;
mbuf[13] = 0x00;
....
mbuf[18+11*cp] = 0x00;
mbuf[19+11*cp] = 0x00;
mbuf[20+11*cp] = 0x00;
mbuf[21+11*cp] = 0x00;
может это не совсем корректно, но все работает отлично
После того как клиент получил список идентов начинают обрабатываться запросы ECM/EMM.
Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
aiZent на форуме
|
|
31.01.2009, 13:44
|
#4
|
Хостер (2,193)
Репа: 289
Регистрация: 24.12.2006
Адрес: Кост.область
 : 289
 : 297 (2)
|
Вот небольшой кусок кода из которого можно выудить все что нужно  .
[spoiler]
Код:
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
#define CWS_FIRSTCMDNO 0xe0
typedef enum
{
MSG_CLIENT_2_SERVER_LOGIN = CWS_FIRSTCMDNO,
MSG_CLIENT_2_SERVER_LOGIN_ACK,
MSG_CLIENT_2_SERVER_LOGIN_NAK,
MSG_CARD_DATA_REQ,
MSG_CARD_DATA,
MSG_SERVER_2_CLIENT_NAME,
MSG_SERVER_2_CLIENT_NAME_ACK,
MSG_SERVER_2_CLIENT_NAME_NAK,
MSG_SERVER_2_CLIENT_LOGIN,
MSG_SERVER_2_CLIENT_LOGIN_ACK,
MSG_SERVER_2_CLIENT_LOGIN_NAK,
MSG_ADMIN,
MSG_ADMIN_ACK,
MSG_ADMIN_LOGIN,
MSG_ADMIN_LOGIN_ACK,
MSG_ADMIN_LOGIN_NAK,
MSG_ADMIN_COMMAND,
MSG_ADMIN_COMMAND_ACK,
MSG_ADMIN_COMMAND_NAK,
MSG_KEEPALIVE = CWS_FIRSTCMDNO + 0x1d
} net_msg_type_t;
typedef enum
{
COMMTYPE_CLIENT,
COMMTYPE_SERVER
} comm_type_t;
typedef struct customData_struct
{
uint16 sid;
} customData_t;
void des_key_parity_adjust(uint8 *key, uint8 len)
{
uint8 i, j, parity;
for (i = 0; i < len; i++)
{
parity = 1;
for (j = 1; j < 8; j++) if ((key[i] >> j) & 0x1) parity = ~parity & 0x01;
key[i] |= parity;
}
}
uint8 *des_key_spread(uint8 *normal)
{
static uint8 spread[16];
spread[ 0] = normal[ 0] & 0xfe;
spread[ 1] = ((normal[ 0] << 7) | (normal[ 1] >> 1)) & 0xfe;
spread[ 2] = ((normal[ 1] << 6) | (normal[ 2] >> 2)) & 0xfe;
spread[ 3] = ((normal[ 2] << 5) | (normal[ 3] >> 3)) & 0xfe;
spread[ 4] = ((normal[ 3] << 4) | (normal[ 4] >> 4)) & 0xfe;
spread[ 5] = ((normal[ 4] << 3) | (normal[ 5] >> 5)) & 0xfe;
spread[ 6] = ((normal[ 5] << 2) | (normal[ 6] >> 6)) & 0xfe;
spread[ 7] = normal[ 6] << 1;
spread[ 8] = normal[ 7] & 0xfe;
spread[ 9] = ((normal[ 7] << 7) | (normal[ 8] >> 1)) & 0xfe;
spread[10] = ((normal[ 8] << 6) | (normal[ 9] >> 2)) & 0xfe;
spread[11] = ((normal[ 9] << 5) | (normal[10] >> 3)) & 0xfe;
spread[12] = ((normal[10] << 4) | (normal[11] >> 4)) & 0xfe;
spread[13] = ((normal[11] << 3) | (normal[12] >> 5)) & 0xfe;
spread[14] = ((normal[12] << 2) | (normal[13] >> 6)) & 0xfe;
spread[15] = normal[13] << 1;
des_key_parity_adjust(spread, 16);
return spread;
}
void des_random_get(uint8 *buffer, uint8 len)
{
uint8 idx = 0;
int randomNo = 0;
for (idx = 0; idx < len; idx++)
{
if (!(idx % 3)) randomNo = rand();
buffer[idx] = (randomNo >> ((idx % 3) << 3)) & 0xff;
}
}
int des_encrypt(uint8 *buffer, int len, uint8 *deskey)
{
uint8 checksum = 0;
uint8 noPadBytes;
uint8 padBytes[7];
char ivec[8];
uint16 i;
if (!deskey) return len;
noPadBytes = (8 - ((len - 1) % ) % 8;
if (len + noPadBytes + 1 >= CWS_NETMSGSIZE-8) return -1;
des_random_get(padBytes, noPadBytes);
for (i = 0; i < noPadBytes; i++) buffer[len++] = padBytes[i];
for (i = 2; i < len; i++) checksum ^= buffer[i];
buffer[len++] = checksum;
des_random_get((uint8 *)ivec, ;
memcpy(buffer+len, ivec, ;
for (i = 2; i < len; i +=
{
cbc_crypt(deskey , (char *) buffer+i, 8, DES_ENCRYPT, ivec);
ecb_crypt(deskey+8, (char *) buffer+i, 8, DES_DECRYPT);
ecb_crypt(deskey , (char *) buffer+i, 8, DES_ENCRYPT);
memcpy(ivec, buffer+i, ;
}
len += 8;
return len;
}
int des_decrypt(uint8 *buffer, int len, uint8 *deskey)
{
char ivec[8];
char nextIvec[8];
int i;
uint8 checksum = 0;
if (!deskey) return len;
if ((len-2) % 8 || (len-2) < 16) return -1;
len -= 8;
memcpy(nextIvec, buffer+len, ;
for (i = 2; i < len; i +=
{
memcpy(ivec, nextIvec, ;
memcpy(nextIvec, buffer+i, ;
ecb_crypt(deskey , (char *) buffer+i, 8, DES_DECRYPT);
ecb_crypt(deskey+8, (char *) buffer+i, 8, DES_ENCRYPT);
cbc_crypt(deskey , (char *) buffer+i, 8, DES_DECRYPT, ivec);
}
for (i = 2; i < len; i++) checksum ^= buffer[i];
if (checksum) return -1;
return len;
}
uint8 *des_login_key_get(uint8 *key1, uint8 *key2)
{
uint8 des14[14];
static uint8 des16[16];
int i;
for (i = 0; i < 14; i++) des14[i] = key1[i] ^ key2[i];
memcpy(des16, des_key_spread(des14), 16);
return des16;
}
int network_message_send(int handle, uint16 *netMsgId, customData_t *customData, uint8 *buffer, int len, uint8 *deskey, comm_type_t commType)
{
uint8 netbuf[CWS_NETMSGSIZE];
if (len < 3 || len + 12 > CWS_NETMSGSIZE || handle < 0) return -1;
buffer[1] = (buffer[1] & 0xf0) | (((len - 3) >> & 0x0f);
buffer[2] = (len - 3) & 0xff;
memcpy(netbuf+12, buffer, len);
len += 12;
if (netMsgId) { if (commType == COMMTYPE_CLIENT) (*netMsgId)++; netbuf[2] = (*netMsgId) >> 8; netbuf[3] = (*netMsgId) & 0xff; }
else netbuf[2] = netbuf[3] = 0;
if (customData)
{
netbuf[4] = customData->sid >> 8;
netbuf[5] = customData->sid & 0xff;
memset(netbuf+6, 0, 6);
}
else memset(netbuf+4, 0, ;
if ((len = des_encrypt(netbuf, len, deskey)) < 0) return -1;
netbuf[0] = (len - 2) >> 8;
netbuf[1] = (len - 2) & 0xff;
write(handle, netbuf, len);
return 0;
}
int network_message_receive(int handle, uint16 *netMsgId, customData_t *customData, uint8 *buffer, uint8 *deskey, comm_type_t commType)
{
int len;
uint8 netbuf[CWS_NETMSGSIZE];
int returnLen;
if (customData) memset(customData, 0, sizeof(customData_t));
if (!buffer || handle < 0) return -1;
len = read(handle, netbuf, 2);
if (!len) return 0;
if (len != 2) return -1;
if (((netbuf[0] << | netbuf[1]) > CWS_NETMSGSIZE - 2) return -1;
len = read(handle, netbuf+2, (netbuf[0] << | netbuf[1]);
if (!len) return 0;
if (len != ((netbuf[0] << | netbuf[1])) return -1;
len += 2;
if ((len = des_decrypt(netbuf, len, deskey)) < 15) return -1;
if ((returnLen = (((netbuf[13] & 0x0f) << | netbuf[14]) + 3) > len-12) return -1;
if (netMsgId)
{
switch (commType)
{
case COMMTYPE_SERVER:
*netMsgId = (netbuf[2] << | netbuf[3];
break;
case COMMTYPE_CLIENT:
if (*netMsgId != ((netbuf[2] << | netbuf[3])) return -1;
break;
default:
return -1;
break;
}
}
if (customData)
{
customData->sid = (netbuf[4] << | netbuf[5];
}
memcpy(buffer, netbuf+12, returnLen);
return returnLen;
}
void network_cmd_no_data_send(int handle, uint16 *netMsgId, customData_t *customData, net_msg_type_t cmd, uint8 *deskey, comm_type_t commType)
{
uint8 buffer[CWS_NETMSGSIZE];
buffer[0] = cmd; buffer[1] = 0;
network_message_send(handle, netMsgId, customData, buffer, 3, deskey, commType);
}
int network_cmd_no_data_receive(int handle, uint16 *netMsgId, customData_t *customData, uint8 *deskey, comm_type_t commType)
{
uint8 buffer[CWS_NETMSGSIZE];
if (network_message_receive(handle, netMsgId, customData, buffer, deskey, commType) != 3) return -1;
return buffer[0];
}
int network_tcp_incoming_port_open(uint16 port)
{
struct sockaddr_in socketAddr;
int socketOptActive = 1;
int handle;
if (!port) return -1;
if ((handle = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "network port %u open: ", port);
perror("socket");
return -1;
}
if (setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &socketOptActive, sizeof(int)) < 0)
{
fprintf(stderr, "network port %u open: error setsockoptn", port);
close(handle);
return -1;
}
socketAddr.sin_family = AF_INET;
socketAddr.sin_port = htons(port);
socketAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(handle, (struct sockaddr *) &socketAddr, sizeof (socketAddr)) < 0)
{
fprintf(stderr, "network port %u open: ", port);
perror("bind");
close(handle);
return -1;
}
if (listen(handle, 5) < 0)
{
fprintf(stderr, "network port %u open: ", port);
perror("listen");
close(handle);
return -1;
}
return handle;
}
int network_tcp_connection_accept(int socketHandle)
{
int connHandle;
struct sockaddr_in peerAddr;
struct sockaddr_in myAddr;
socklen_t peerAddrLen;
socklen_t myAddrLen;
uint16 peerPort, myPort;
uint32 peerIp, myIp;
if (socketHandle < 0) return -1;
peerAddrLen = sizeof(peerAddr);
myAddrLen = sizeof(myAddr);
if ((connHandle = accept(socketHandle, (struct sockaddr *) &peerAddr, &peerAddrLen)) < 0) { fprintf(stderr, "error network accept connectionn"); return -1; }
peerPort = ntohs(peerAddr.sin_port);
peerIp = ntohl(peerAddr.sin_addr.s_addr);
myPort = ntohs(myAddr.sin_port);
myIp = ntohl(myAddr.sin_addr.s_addr);
/* optional: do checks on or log IP of incoming connections */
return connHandle;
}
int network_tcp_connection_open(uint8 *hostname, uint16 port)
{
int handle;
struct hostent *hostaddr;
struct sockaddr_in socketAddr;
if (!(hostaddr = gethostbyname(hostname))) { fprintf(stderr, "Host lookup of %s failedn", hostname); return -1; }
if ((handle = socket(PF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "network make connection: couldn't create socketn"); return -1; }
socketAddr.sin_family = AF_INET;
socketAddr.sin_port = htons(port);
socketAddr.sin_addr.s_addr = ((struct in_addr *)hostaddr->h_addr)->s_addr;
if (connect(handle, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0) { fprintf(stderr, "network make connection: error connectn"); close(handle); return -1; }
return handle;
}
[/spoiler]
Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
Основные команды запроса/ответа ECM/DW опишу чуть позже, к клиентам надо ехать  .
Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
aiZent на форуме
|
|
|
Эти 5 пользователя(ей) сказали Спасибо aiZent за это полезное сообщение:
|
|
27.03.2009, 08:46
|
#5
|
Igorkn
|
Возможно ли сделать шаринг-сервер так чтобы он мог из какого-нибудь softcam.key брать ключи biss и отправлять их клиенту. Есть сервер c DVB платами вещающий каналы в небольшую локальную сеть, на нем установлено клиентское ПО для шаринга по протоколу newcamd, соответственно для каждого канала свой логин пароль. Т.к. biss не работает через шаринг то проблема с открытием каналов в данной кодировке.
|
|
|
27.03.2009, 11:59
|
#6
|
Хостер (2,193)
Репа: 289
Регистрация: 24.12.2006
Адрес: Кост.область
 : 289
 : 297 (2)
|
Ну по идее biss это даже не кодировка, кодировка CSA, а так называемый BISS это просто DW (половинка) в открытом виде, соответственно, с таким же успехом можно посылать постоянно одну и ту же DW-ку, трудность в том, как определить DW для какого канала нужно посылать? Никогда не интересовался, летят ли ECM для каналов кодированных в "BISS", если не летят, тогда уже будет проблемно.
p.s. Итог: Написать сервер несложно, можно даже сделать чтобы с каждого отдельного порта отдавалась только одна DW-ка (каждый канал на отдельном порте), другое дело как заставить ресивер выполнить запрос DW-ки
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
aiZent на форуме
|
|
|
Этот пользователь сказал Спасибо aiZent за это полезное сообщение:
|
|
04.06.2009, 03:06
|
#7
|
Новичок (9)
Репа: 11
Регистрация: 04.06.2009
 : 0
 : 0 (2)
|
Хорошая статья ! но плохо что нету продолжения .
Интересно юзеры сверяются с базой , а вот с какой ? можно же прописать в ТХТ , а можно и в MySQL .
|
Horitonov вне форума
|
|
04.06.2009, 10:21
|
#8
|
Хостер (2,193)
Репа: 289
Регистрация: 24.12.2006
Адрес: Кост.область
 : 289
 : 297 (2)
|
А это уже как реализуешь
[spoiler]
хорошо быть программером, можно сделать так, или эдак, короч так как хочешь  )
[/spoiler]
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
aiZent на форуме
|
|
04.06.2009, 17:59
|
#9
|
Новичок (9)
Репа: 11
Регистрация: 04.06.2009
 : 0
 : 0 (2)
|
Ясно , плохо что весь код на С++ так как начал изучать Делфи уже купил пару книг сижу и читаю .
aiZent опиши основные команды запроса/ответа ECM/DW
|
Horitonov вне форума
|
|
04.06.2009, 21:35
|
#10
|
Хостер (2,193)
Репа: 289
Регистрация: 24.12.2006
Адрес: Кост.область
 : 289
 : 297 (2)
|
[spoiler]
Весь код на С/С++ ну может быть еще Java (если после последних событий (лицензирование эффективного сборщика мусора) не сдохнет) и будет в будущем, за Delphi только островки коммьюнити останутся  ) борланд практически умер.
А С/С++ отраслевой стандарт  .
[/spoiler]
Цитата:
|
Сообщение от Horitonov;undefined
основные команды запроса/ответа ECM/DW
|
В протоколе Newcamd?
__________________
Хостинг Казахстан/Мегалайн/Костанай, в т.ч. VPS/VDS.
|
aiZent на форуме
|
|
| Опции темы |
Поиск в этой теме |
|
|
|
| Опции просмотра |
Линейный вид
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
|
Часовой пояс GMT +4, время: 08:51. |
|
|