#define _GNU_SOURCE #include #include #include #include #include #include "common.h" typedef struct LocationInfo LocationInfo; typedef enum { ITEM_PERFORM_ACTION, ITEM_PERFORM_TARGETED_ACTION, ITEM_MOVE_TO_LOCATION, ITEM_UNWRAP, ITEM_NAMED_UNWRAP } ItemType; typedef struct { void (*func)(int s); bool showDesc; } ActionInfo; typedef struct ItemInfo { char* article; char* name; char* message; LocationInfo* requiredLocation; ItemType type; union { void* data; LocationInfo* newLocation; struct ItemInfo* newItem; struct { ActionInfo* actionInfo; char* requiredTarget; } action; struct { char* name; struct ItemInfo* newItem; } namedUnwrap; } info; } ItemInfo; typedef struct { char* direction; LocationInfo* target; } MovementInfo; struct LocationInfo { char* text; MovementInfo* movements; ItemInfo items[]; }; char* USER = "hackquest"; int LPORT = 7331; char name[32]; LocationInfo* location; size_t inventory_count = 0; ItemInfo* inventory = NULL; extern LocationInfo livingArea; extern LocationInfo bedroom; void hackTheGibson(int s); void exitGame(int s); MovementInfo outsideMovements[] = {{"north", &livingArea}, {NULL, NULL}}; LocationInfo outside = { "You are outside. Why are you outside? You have work to do! Keep out of\n" "direct sunlight! To the north is the door to your apartment.\n", outsideMovements, {{NULL, NULL, NULL, NULL, 0, {NULL}}} }; ActionInfo exitAction = {exitGame, false}; MovementInfo gibsonMovements[] = {{NULL, NULL}}; LocationInfo gibson = { "You have a shell on the Gibson. You are root. It seems everything is\n" "here, including the source to the virus. Called 'Da Vinci'. In the\n" "directory called 'garbage file'. Someone in that company has been\n" "watching too many movies to understand how to secure a server.\n\n", gibsonMovements, {{"the", "source code to the virus", "The source code has everything, including passwords, for controlling the\n" "virus. Now you and your friend have everything you need to stop them.\n" "Hack the planet?\n\n" "A real hacker would have won the game by pwning the game itself.\n", NULL, ITEM_PERFORM_ACTION, {&exitAction}}, {NULL, NULL, NULL, NULL, 0, {NULL}}} }; ActionInfo hackTheGibsonAction = {hackTheGibson, true}; ItemInfo zeroDayItem = {"an", "0-day", "", NULL, ITEM_PERFORM_TARGETED_ACTION, {.action = {&hackTheGibsonAction, "Gibson"}}}; MovementInfo websiteMovements[] = {{"offline", &bedroom}, {NULL, NULL}}; LocationInfo website = { "You are on your computer logged in to your friend's secret forum. It has\n" "a single post and a download link:\n\n" "I found out that this binary is running on ELF Technologies' main server,\n" "called the Gibson (of course). It has to have a bug in it, it's a pretty\n" "big custom job. Can you help me find the bug? We might be able to get\n" "inside their network with this.\n\n" "-ihackatlife\n\n", websiteMovements, {{"a", "binary executable", "You open the binary in IDA and within the hour you spot an unauthenticated\n" "stack-based buffer overflow. Easy enough.\n\n", NULL, ITEM_UNWRAP, {&zeroDayItem}}, {NULL, NULL, NULL, NULL, 0, {NULL}}} }; ItemInfo passwordItem = {"a", "password to your friend's website", "The password was accepted.\n\n", &bedroom, ITEM_MOVE_TO_LOCATION, {&website}}; MovementInfo livingAreaMovements[] = {{"south", &outside}, {"north", &bedroom}, {NULL, NULL}}; LocationInfo livingArea = { "You are in the living area. Your recent mail is on the table. To the\n" "north is the door to your bedroom. To the south is the door to the outside.\n", livingAreaMovements, {{"a", "letter addressed to %s", "I don't know how else to reach you. You haven't been online in a long time.\n" "I need your help. I heard that the guys up at ELF Technologies are going to\n" "unleash a nasty virus on the world any time now. It is designed to infect\n" "everything and destroy the hardware it's running on, and it's infiltrating\n" "social networks to find as many targets as it can. I don't know what crazy\n" "business model they have for getting money out of that, but it is what it\n" "is, and we need to stop it. Go to my website and enter the password\n" "'elfpwnage' in the hidden form, and we'll continue the discussion there.\n\n" "-ihackatlife\n\n", NULL, ITEM_NAMED_UNWRAP, {.namedUnwrap = {name, &passwordItem}}}, {NULL, NULL, NULL, NULL, 0, {NULL}}} }; MovementInfo bedroomMovements[] = {{"south", &livingArea}, {NULL, NULL}}; LocationInfo bedroom = { "You are in your bedroom. There is a computer on your desk. To the south is\n" "the door leading to the rest of the apartment.\n", bedroomMovements, {{NULL, NULL, NULL, NULL, 0, {NULL}}} }; MovementInfo startMovements[] = {{NULL, NULL}}; LocationInfo start = { "You are in your bedroom. The room is spinning and you are very tired.\n", startMovements, {{"a", "can of Red Bull", "You feel refreshed.\n\n", &start, ITEM_MOVE_TO_LOCATION, {&bedroom}}, {NULL, NULL, NULL, NULL, 0, {NULL}}} }; void hackTheGibson(int s) { sendMsg(s, "You hacked the Gibson!\n\n"); location = &gibson; } void exitGame(int s) { (void)s; exit(0); } void showItem(int s, ItemInfo* item) { if (item->type == ITEM_NAMED_UNWRAP) { char str[128]; snprintf(str, 128, item->name, item->info.namedUnwrap.name); sendMsgf(s, "There is %s %s here.\n", item->article, str); } else { sendMsgf(s, "There is %s %s here.\n", item->article, item->name); } } bool look(int s) { if (!location->items[0].name) sendMsgf(s, "There is nothing here.\n"); else { for (size_t i = 0; location->items[i].name; i++) showItem(s, &location->items[i]); } return false; } void addToInventory(ItemInfo* item) { inventory = realloc(inventory, (inventory_count + 1) * sizeof(ItemInfo)); memcpy(&inventory[inventory_count], item, sizeof(ItemInfo)); if (item->type == ITEM_NAMED_UNWRAP) { inventory[inventory_count].name = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); snprintf(inventory[inventory_count].name, 4095, item->name, item->info.namedUnwrap.name); } inventory_count++; } bool get(int s, char* arg) { if ((!arg) || (!arg[0])) sendMsg(s, "Get what?\n"); else { ItemInfo* item = NULL; size_t idx; char* msg = "I don't see that here.\n"; for (size_t i = 0; location->items[i].name; i++) { if (!strncasecmp(location->items[i].name, arg, strlen(arg))) { if (item) { msg = "I'm confused. More than one item here could go by that.\n"; item = NULL; break; } item = &location->items[i]; idx = i; } } if (!item) sendMsg(s, msg); else { addToInventory(&location->items[idx]); for (size_t j = idx; location->items[j].name; j++) memcpy(&location->items[j], &location->items[j + 1], sizeof(ItemInfo)); } } return true; } bool use(int s, char* arg, char* target) { bool showDesc = false; if ((!arg) || (!arg[0])) sendMsg(s, "Use the force?\n"); else { ItemInfo* item = NULL; size_t idx; char* msg = "Trying to use what you don't have will get you nowhere in life.\n"; for (size_t i = 0; i < inventory_count; i++) { if (!strncasecmp(inventory[i].name, arg, strlen(arg))) { if (item) { msg = "I'm confused. Which one do you want?\n"; item = NULL; break; } item = &inventory[i]; idx = i; } } if (!item) sendMsg(s, msg); else if ((item->requiredLocation) && (item->requiredLocation != location)) sendMsg(s, "You can't use that here.\n"); else { bool ok = true; if (target) { if ((item->type == ITEM_PERFORM_ACTION) || (item->type == ITEM_MOVE_TO_LOCATION) || (item->type == ITEM_UNWRAP)) { sendMsg(s, "That item can't have a target.\n"); ok = false; } else if (!strcasecmp(item->info.action.requiredTarget, target)) { sendMsg(s, item->message); item->info.action.actionInfo->func(s); showDesc = item->info.action.actionInfo->showDesc; } else { sendMsg(s, "Your target is invalid for that item.\n"); ok = false; } } else { sendMsg(s, item->message); switch (item->type) { case ITEM_PERFORM_ACTION: item->info.action.actionInfo->func(s); showDesc = item->info.action.actionInfo->showDesc; break; case ITEM_PERFORM_TARGETED_ACTION: sendMsg(s, "You need a target for that item.\n"); ok = false; break; case ITEM_MOVE_TO_LOCATION: location = item->info.newLocation; showDesc = true; break; case ITEM_UNWRAP: inventory = realloc(inventory, (inventory_count + 1) * sizeof(ItemInfo)); memcpy(&inventory[inventory_count], item->info.newItem, sizeof(ItemInfo)); inventory_count++; showDesc = true; break; case ITEM_NAMED_UNWRAP: inventory = realloc(inventory, (inventory_count + 1) * sizeof(ItemInfo)); memcpy(&inventory[inventory_count], item->info.namedUnwrap.newItem, sizeof(ItemInfo)); inventory_count++; showDesc = true; break; } } if (ok) { for (size_t j = idx; j < (inventory_count - 1); j++) memcpy(&inventory[j], &inventory[j + 1], sizeof(ItemInfo)); inventory_count--; } } } return showDesc; } bool go(int s, char* arg) { bool showDesc = false; if ((!arg) || (!arg[0])) sendMsg(s, "Go where?\n"); else { MovementInfo* dir = NULL; char* msg = "I don't know where you're talking about.\n"; for (size_t i = 0; location->movements[i].direction; i++) { if (!strncasecmp(location->movements[i].direction, arg, strlen(arg))) { if (dir) { msg = "I'm confused. Which way now?\n"; dir = NULL; break; } dir = &location->movements[i]; } } if (!dir) sendMsg(s, msg); else { location = dir->target; showDesc = true; } } return showDesc; } int handleConnection(int s) { sendMsg(s, "The 1337 Adventures of an Unknown Hacker\n\n" "What is your name? "); int result = readUntil(s, name, 31, '\n'); if (result < 0) return 0; name[result] = 0; sendMsgf(s, "Welcome, %s, to your adventure.\n\n", name); location = &start; bool showDesc = true; while (true) { if (showDesc) { sendMsg(s, location->text); for (size_t i = 0; i < inventory_count; i++) sendMsgf(s, "You have %s %s.\n", inventory[i].article, inventory[i].name); } showDesc = false; sendMsg(s, "> "); char cmd[256]; result = readUntil(s, cmd, 255, '\n'); if (result < 0) break; cmd[result] = 0; char* space = strchr(cmd, ' '); char* onPtr = strstr(cmd, " on "); char* arg; if (!space) arg = NULL; else { *space = 0; arg = space + 1; } char* target; if (!onPtr) target = NULL; else { *onPtr = 0; target = onPtr + 4; } if (!strcasecmp(cmd, "look")) showDesc = look(s); else if (!strcasecmp(cmd, "get")) showDesc = get(s, arg); else if (!strcasecmp(cmd, "use")) showDesc = use(s, arg, target); else if (!strcasecmp(cmd, "go")) showDesc = go(s, arg); else if (!strcasecmp(cmd, "quit")) { sendMsg(s, "What? Too lame to pwn anything?\n"); break; } else if (!cmd[0]) showDesc = true; else sendMsg(s, "I don't understand.\n"); } return 0; }