import os from threading import Thread from script.obfuscate import BaseObfuscation, NotInstalledError import re import pathlib import shutil current_dir = pathlib.Path(__file__).parent.absolute() shapeshifter_path = current_dir.joinpath("binary/shapeshifter-dispatcher") shapeshifter_binary = shapeshifter_path.joinpath("shapeshifter-dispatcher") class GoNotFound(Exception): pass class GoVersionIncompatible(Exception): pass class GitNotInstalled(Exception): pass class ShapeShifterBase(BaseObfuscation): MODES_SUPPORTED = ["server", "client"] ALGORITHMS_SUPPORTED = ["obfs4"] def __init__(self, mode, algorithm, wireguard_port=None, listen_port=None, client_replicant_port=None, client_options=None): super().__init__( binary_path=shapeshifter_binary, algorithm=algorithm ) assert mode in ShapeShifterBase.MODES_SUPPORTED, "%s is not a supported mode. Supported: %s" % ( mode, ShapeShifterBase.MODES_SUPPORTED ) self._wireguard_port = wireguard_port self._listen_port = listen_port self._client_replicant_port = client_replicant_port self._mode = mode self._client_options = client_options self._go_version_min = (1, 14, 0) self.ensure_installed() def _verify_go_installed(self): output, code = self.execute("version", override_command="go") try: match = re.findall("go([0-9]+.[0-9]+.[0-9]+)", output) match = match[0] except IndexError: raise GoNotFound("Go was not found on the system.") major, minor, patch = match.split(".") if int(major) < self._go_version_min[0] or int(minor) < self._go_version_min[1] or \ int(patch) < self._go_version_min[2]: raise GoVersionIncompatible("Go version is incompatible. %s < %s" % (self._go_version_min, match)) def _verify_git_installed(self): output, code = self.execute("version", override_command="git") if code == 0: return raise GitNotInstalled("Git does not seem to be installed. Code: %s, Output: %s" % (code, output)) def _verify_shapeshifter_installed(self): output, code = self.execute("version", override_command=shapeshifter_binary) print(output, code) def _install_shapeshifter(self): if shapeshifter_path.is_dir(): shutil.rmtree(shapeshifter_path) output, code = self.execute( "clone", "https://github.com/OperatorFoundation/shapeshifter-dispatcher.git", shapeshifter_path, override_command="git") assert code == 0, "Git exited with error. %s" % (output,) current_working_dir = os.getcwd() os.chdir(shapeshifter_path) output, code = self.execute("build", override_command="go") os.chdir(current_working_dir) assert code == 0, "Building shapeshifter failed with output: %s" % (output,) def ensure_installed(self): try: super().ensure_installed() except NotInstalledError: self._verify_go_installed() self._verify_git_installed() try: self._verify_shapeshifter_installed() except FileNotFoundError: self._install_shapeshifter() def start(self): bind_command = "bindaddr" if self._mode == "server" else "proxylistenaddr" bind_port = self._listen_port if self._mode == "server" else self._client_replicant_port bind_value = f"127.0.0.1:{bind_port}" # TODO if self._mode == "server": bind_value = self.algorithm + "-" + bind_value connect_command = "orport" if self._mode == "server" else "target" connect_port = self._wireguard_port if self._mode == "server" else self._listen_port cmd = [ "-transparent", "-udp", f"-{self._mode}", "-state", f"state", f"-{connect_command}", f"127.0.0.1:{connect_port}", "-transports", self.algorithm, f"-{bind_command}", bind_value ] if self._mode == "client": cmd.extend([ "-options", f"{self._client_options}" ]) cmd.extend([ "-logLevel", "DEBUG", "-enableLogging", ]) print(*cmd) output, code = self.execute(*cmd, stream=True, prefix=self._mode) print(output, code) def start_threaded(self): t = Thread(target=self.start, args=()) t.start() return t if __name__ == "__main__": import time x = ShapeShifterBase( mode="server", algorithm="obfs2", wireguard_port=3333, listen_port=2222, ) x.start_threaded() time.sleep(1) x = ShapeShifterBase( mode="client", algorithm="obfs2", listen_port=2222, client_replicant_port=1443, client_options='{"cert": "BWvGMVn3C8daXai2Xo+If23XS94eztZE9Kbtykvy9x5ADWc6YCHdGlWQfDh1fzu7AhuTIA", "iat-mode": "0"}' ) x.start_threaded() while True: time.sleep(1)