关于AirPlay的一些逆向研究成果。
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.
 
 
 
 
 

196 lines
4.3 KiB

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <syslog.h>
#include "fairplay.h"
#define MAX_LISTEN_NUM 5
#define SEND_BUF_SIZE 1024
#define RECV_BUF_SIZE 1024
#define LISTEN_PORT 19999
//#define RUN_DAEMON
#ifdef DEBUG
#ifdef RUN_DAEMON
#define dprintf(fmt, args...) syslog(LOG_DEBUG, fmt, ##args)
#else
#define dprintf printf
#endif
#else
#define dprintf(...)
#endif
#ifdef RUN_DAEMON
#define myprintf(fmt, args...) syslog(LOG_INFO, fmt, ##args)
#else
#define myprintf(fmt, args...) fprintf(stderr, fmt, ##args)
#endif
static void print_buf(unsigned char *buf, int len)
{
#ifdef DEBUG
dprintf("buflen:%d\n", len);
int i;
for (i = 0; i < len; i++) {
dprintf("0x%02x ", buf[i]);
}
dprintf("\n");
#endif
}
static int correct_len[] = { 18, 166, 74 };
void *handle_one_client(void *arg)
{
int ret;
unsigned char sendbuf[SEND_BUF_SIZE] = { 0 };
unsigned char recvbuf[RECV_BUF_SIZE] = { 0 };
int sendlen = 0;
int recvlen = 0;
int cmd;
unsigned char c;
unsigned char *outbuf = NULL;
int n = 0;
int app_sock = *(int*)arg;
unsigned char hwinfo[24];
unsigned char *sapbuf = NULL;
struct timeval tv;
tv.tv_sec = 10; /* 10 Secs Timeout */
tv.tv_usec = 0; // Not init'ing this can cause strange errors
setsockopt(app_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
do {
n++;
ret = 0;
//receive data
recvlen = RECV_BUF_SIZE - 1;
// in theory there are chances that the packet is fragmented by routers,
// but since the packets are small and if it happens client can just retry
// we don't bother to write a more robust receive func
recvlen = recv(app_sock, recvbuf, recvlen, 0);
if (recvlen <= 0) {
dprintf("recv error, errno=%d\n", errno);
ret = 0 - n;
} else {
unsigned int len;
cmd = recvbuf[0];
len = recvbuf[1];
dprintf("recv cmd=%d, len = %d, recvlen = %d\n", cmd, len, recvlen);
if (cmd != n || recvlen != len || len != correct_len[n-1]) {
dprintf("Invalid input, cmd=%d, len = %d, recvlen = %d\n",
cmd, len, recvlen);
ret = 0 - n;
}
print_buf(recvbuf, recvlen);
}
if (ret != 0) break;
switch (n) {
case 1:
airplay_load_hwinfo(hwinfo);
airplay_initialize_sap(hwinfo, &sapbuf);
airplay_process_fpsetup_message(3, hwinfo, sapbuf,
recvbuf + 2, &outbuf, &sendlen, &c);
break;
case 2:
airplay_process_fpsetup_message(3, hwinfo, sapbuf,
recvbuf + 2, &outbuf, &sendlen, &c);
break;
case 3:
airplay_get_encryption_key(sapbuf, recvbuf + 2, recvlen - 2,
&outbuf, &sendlen);
break;
}
if (outbuf) {
memcpy(sendbuf, outbuf, sendlen);
print_buf(sendbuf, sendlen);
send(app_sock, sendbuf, sendlen, 0);
free(outbuf);
} else {
dprintf("Invalid output\n");
ret = -4;
break;
}
} while ( n < 3);
if (sapbuf) free(sapbuf);
close(app_sock);
myprintf("Done\n");
}
int main()
{
int listen_sock = 0;
int app_sock = 0;
struct sockaddr_in hostaddr;
struct sockaddr_in clientaddr;
int socklen = sizeof(clientaddr);
pthread_t thread_id;
pthread_attr_t attr;
myprintf("Starting fairplay server\n");
#ifdef RUN_DAEMON
if (daemon(0, 0) != 0)
myprintf("daemonlize failed!\n");
#endif
memset((void *)&hostaddr, 0, sizeof(hostaddr));
memset((void *)&clientaddr, 0, sizeof(clientaddr));
hostaddr.sin_family = AF_INET;
hostaddr.sin_port = htons(LISTEN_PORT);
hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0) {
printf("%s:%d, create socket failed", __FILE__, __LINE__);
exit(1);
}
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, NULL, 0);
if (bind(listen_sock, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) <
0) {
printf("%s:%d, bind socket failed", __FILE__, __LINE__);
exit(1);
}
if (listen(listen_sock, MAX_LISTEN_NUM) < 0) {
printf("%s:%d, listen failed", __FILE__, __LINE__);
exit(1);
}
while (1) {
app_sock =
accept(listen_sock, (struct sockaddr *)&clientaddr,
&socklen);
if (app_sock < 0) {
printf("%s:%d, accept failed", __FILE__, __LINE__);
continue;
} else {
myprintf("accepted client from %s\n", inet_ntoa(clientaddr.sin_addr));
}
pthread_create(&thread_id, NULL, &handle_one_client,(void*)&app_sock);
}
close(listen_sock);
return 0;
}