diff --git a/Dockerfile b/Dockerfile index d72aae9d..ef73dac5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,8 +34,9 @@ WORKDIR /src # Copy all files to build COPY . . -# Publish de4dot targeting net8.0 +# Publish both de4dot and the de4dot.mcp server targeting net8.0 RUN dotnet publish -c Release -f net8.0 -o /app/publish/de4dot de4dot/de4dot.csproj +RUN dotnet publish -c Release -f net8.0 -o /app/publish/mcp de4dot.mcp/de4dot.mcp.csproj RUN rm -rf /app/publish/**/*.pdb /app/publish/**/*.xml # ============================================================================== @@ -44,20 +45,35 @@ RUN rm -rf /app/publish/**/*.pdb /app/publish/**/*.xml FROM mcr.microsoft.com/dotnet/runtime:8.0 AS runtime WORKDIR /app -# Install native dependencies required for execution (e.g. globalization) +# Install native dependencies required for execution (e.g. globalization, bash) RUN apt-get update && apt-get install -y \ libicu-dev \ + bash \ && rm -rf /var/lib/apt/lists/* -# Copy de4dot published folder +# Copy de4dot and MCP published folders COPY --from=dotnet-builder /app/publish/de4dot ./de4dot +COPY --from=dotnet-builder /app/publish/mcp ./mcp # Copy compiled libBeaEngine.so from Stage 1 standardized path to system libraries & register COPY --from=beaengine-builder /app/libBeaEngine.so /usr/local/lib/libBeaEngine.so RUN ldconfig -# Create symlink for global accessibility -RUN ln -s /app/de4dot/de4dot /usr/local/bin/de4dot +# Create symlinks for global accessibility +RUN ln -s /app/de4dot/de4dot /usr/local/bin/de4dot && \ + ln -s /app/mcp/de4dot.mcp /usr/local/bin/de4dot-mcp -ENTRYPOINT ["de4dot"] +# Create a unified entrypoint script inside the final image +RUN printf '#!/bin/bash\n\ +if [ "$1" = "--mcp" ] || [ "$1" = "-mcp" ]; then\n\ + shift\n\ + exec /app/mcp/de4dot.mcp "$@"\n\ +else\n\ + exec /app/de4dot/de4dot "$@"\n\ +fi\n' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh + +# Expose HTTP port 8080 (for MCP Web API mode) +EXPOSE 8080 + +ENTRYPOINT ["/app/entrypoint.sh"] CMD ["--help"] diff --git a/README.md b/README.md index 5510f0c7..14fc8eb1 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,13 @@ Binaries Get binaries from the build server [![](https://github.com/GDATAAdvancedAnalytics/de4dotEx/workflows/CI%20build/badge.svg)](https://github.com/GDATAAdvancedAnalytics/de4dotEx/actions). -Docker Support -============== +Docker & MCP Server Support +=========================== -de4dotEx now has fully integrated **Docker containerization** support: +de4dotEx now has fully integrated **Docker containerization** and native **Model Context Protocol (MCP)** server support: -* **Docker Container:** Build a single container to run de4dotEx natively or inside a cross-platform headless Wine environment. See the [Docker Containerization](#docker-containerization) section below for build and run instructions. +* **Docker Container:** Build a single container to run de4dotEx natively, as a local stdio MCP server, or as an HTTP Web API microservice. See the [Docker Containerization](#docker-containerization) section below for build and run instructions. +* **MCP Server:** Integrate de4dotEx directly with AI assistants like Claude Desktop, Cursor, or Windsurf to programmatically deobfuscate binaries. See the [Native C# MCP Server](#native-c-mcp-server) section below for configuration instructions. It's FREE but there's NO SUPPORT ================================ @@ -353,3 +354,64 @@ If you get a warning saying `DEPRECATED: The legacy builder is deprecated` or an ### Globalization/Localization Issues * Both images are configured with native globalization components (`libicu`). If you encounter encoding issues with non-ASCII or obfuscated class/method names, make sure your terminal and volume directories are UTF-8 compliant. + + +Native C# MCP Server +==================== + +de4dotEx includes a native **Model Context Protocol (MCP)** server built on **.NET 8.0**. + +By running de4dotEx as an MCP server, you can connect it directly to AI assistants (such as **Claude Desktop**, **Cursor IDE**, **Windsurf**, or the **VS Code MCP Client**). This allows the AI agent to programmatically and automatically deobfuscate .NET assemblies (EXE and DLL) inside directories it is analyzing! + +Exposed MCP Tools +----------------- + +### `deobfuscate` +Deobfuscate and unpack a .NET assembly (EXE or DLL) using de4dotEx. + +**Arguments:** +* `file_path` (string, **required**): The absolute file path of the .NET assembly to deobfuscate. +* `output_path` (string, *optional*): Custom file path where the cleaned assembly should be written. +* `options` (string, *optional*): Additional CLI options to pass to the engine (e.g. `"--preserve-tokens"` or `"-str delegate"`). + +How to Build the MCP Server +--------------------------- + +You can build the MCP server using the standard `dotnet` CLI: +```bash +dotnet publish -c Release -f net8.0 -o ./publish-net8.0-mcp de4dot.mcp +``` +After building, the published files (including the binary and its dependencies) will be placed in the `./publish-net8.0-mcp/` directory. + +How to Configure Your AI Client +------------------------------- + +To add de4dotEx to your AI assistant, register it in your client's MCP configuration file (typically `claude_desktop_config.json` for Claude Desktop). + +### 1. Claude Desktop Configuration +Open your Claude Desktop configuration file: +* **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` +* **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` + +Add the following block under the `mcpServers` object: +```json +{ + "mcpServers": { + "de4dotex": { + "command": "dotnet", + "args": [ + "/absolute/path/to/de4dotEx/publish-net8.0-mcp/de4dot.mcp.dll" + ] + } + } +} +``` +*(Make sure to replace `/absolute/path/to/de4dotEx/` with the actual absolute path to your cloned repository.)* + +### 2. Cursor IDE Configuration +1. Open Cursor Settings -> **Features** -> **MCP**. +2. Click **+ Add New MCP Server**. +3. Name: `de4dotex` +4. Type: `stdio` +5. Command: `dotnet /absolute/path/to/de4dotEx/publish-net8.0-mcp/de4dot.mcp.dll` +} diff --git a/build.ps1 b/build.ps1 index 58b0a085..a0259d58 100644 --- a/build.ps1 +++ b/build.ps1 @@ -7,3 +7,7 @@ Remove-Item Release\net48\*.pdb, Release\net48\*.xml, Release\net48\Test.Rename. dotnet publish -c Release -f net8.0 -o publish-net8.0 de4dot if ($LASTEXITCODE) { exit $LASTEXITCODE } Remove-Item publish-net8.0\*.pdb, publish-net8.0\*.xml + +dotnet publish -c Release -f net8.0 -o publish-net8.0-mcp de4dot.mcp +if ($LASTEXITCODE) { exit $LASTEXITCODE } +Remove-Item publish-net8.0-mcp\*.pdb, publish-net8.0-mcp\*.xml diff --git a/de4dot.mcp/Program.cs b/de4dot.mcp/Program.cs new file mode 100644 index 00000000..8f6a390d --- /dev/null +++ b/de4dot.mcp/Program.cs @@ -0,0 +1,274 @@ +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Hosting; + +namespace de4dot.mcp { + class Program { + static async Task Main(string[] args) { + // Check if user requested HTTP Web API mode + if (args.Length > 0 && (args[0] == "--http" || args[0] == "-h")) { + int port = 8080; + if (args.Length > 1 && int.TryParse(args[1], out int parsedPort)) { + port = parsedPort; + } + await StartHttpServer(port); + } else { + // Otherwise run in standard Stdio-based MCP mode + await StartMcpStdioLoop(); + } + } + + #region HTTP Web API Mode + static async Task StartHttpServer(int port) { + var builder = WebApplication.CreateBuilder(); + builder.WebHost.ConfigureKestrel(options => { + options.ListenAnyIP(port); + }); + + var app = builder.Build(); + + // Root info route + app.MapGet("/", () => $"de4dotEx Web API is active and listening on port {port}!\n\nTo deobfuscate, make a POST request to /deobfuscate with a 'file' parameter containing your obfuscated assembly."); + + // Main /deobfuscate POST file handler + app.MapPost("/deobfuscate", async (HttpContext context) => { + if (!context.Request.HasFormContentType) { + return Results.BadRequest("Error: Request must be multipart/form-data containing a 'file' field."); + } + + var form = await context.Request.ReadFormAsync(); + var file = form.Files.GetFile("file"); + + if (file == null || file.Length == 0) { + return Results.BadRequest("Error: No file uploaded under form-key 'file'."); + } + + // 1. Create unique paths in the Temp directory + var tempId = Guid.NewGuid().ToString("N"); + var tempInput = Path.Combine(Path.GetTempPath(), $"{tempId}_{file.FileName}"); + var tempOutput = Path.Combine(Path.GetTempPath(), $"{tempId}_cleaned_{file.FileName}"); + + // 2. Stream uploaded file directly to disk + using (var stream = new FileStream(tempInput, FileMode.Create)) { + await file.CopyToAsync(stream); + } + + try { + // 3. Build native arguments list + var argsList = new List { tempInput, "-o", tempOutput }; + + // Read optional 'options' form field and append if present + var extraOptions = form["options"].ToString(); + if (!string.IsNullOrEmpty(extraOptions)) { + var tokens = extraOptions.Split(' ', StringSplitOptions.RemoveEmptyEntries); + argsList.AddRange(tokens); + } + + // 4. Invoke de4dotEx's native engine in-process + int exitCode = de4dot.cui.Program.Main(argsList.ToArray()); + + if (exitCode != 0 || !File.Exists(tempOutput)) { + return Results.Problem("Deobfuscation failed inside the de4dotEx analysis engine."); + } + + // 5. Read the processed bytes and send back as binary file stream + var fileBytes = await File.ReadAllBytesAsync(tempOutput); + return Results.File(fileBytes, "application/octet-stream", $"cleaned_{file.FileName}"); + } + catch (Exception ex) { + return Results.Problem($"An unexpected error occurred during deobfuscation: {ex.Message}"); + } + finally { + // 5. Always securely clean up our temp files + try { + if (File.Exists(tempInput)) File.Delete(tempInput); + if (File.Exists(tempOutput)) File.Delete(tempOutput); + } + catch { } + } + }); + + Console.WriteLine($"[de4dotEx] Starting HTTP Web API Server on port {port}..."); + await app.RunAsync(); + } + #endregion + + #region Stdio MCP Mode + static async Task StartMcpStdioLoop() { + // Set input and output streams to UTF-8 + Console.InputEncoding = Encoding.UTF8; + Console.OutputEncoding = Encoding.UTF8; + + var reader = Console.In; + var writer = Console.Out; + + while (true) { + var line = await reader.ReadLineAsync(); + if (line == null) break; + + try { + var jsonNode = JsonNode.Parse(line); + if (jsonNode == null) continue; + + var method = jsonNode["method"]?.ToString(); + var id = jsonNode["id"]?.ToString(); + + if (method == "initialize") { + SendResponse(writer, id, new { + protocolVersion = "2024-11-05", + capabilities = new { + tools = new { } + }, + serverInfo = new { + name = "de4dotEx", + version = "1.0.0" + } + }); + } + else if (method == "notifications/initialized") { + // Client notification. No response required. + } + else if (method == "tools/list") { + SendResponse(writer, id, new { + tools = new[] { + new { + name = "deobfuscate", + description = "Deobfuscate and unpack a .NET assembly (EXE or DLL) using de4dotEx", + inputSchema = new { + type = "object", + properties = new { + file_path = new { + type = "string", + description = "The absolute file path of the .NET assembly to deobfuscate." + }, + output_path = new { + type = "string", + description = "Optional custom output path for the deobfuscated assembly." + }, + options = new { + type = "string", + description = "Optional extra CLI arguments to pass directly to de4dotEx (e.g. '-str delegate' or '--preserve-tokens')." + } + }, + required = new[] { "file_path" } + } + } + } + }); + } + else if (method == "tools/call") { + var toolName = jsonNode["params"]?["name"]?.ToString(); + if (toolName == "deobfuscate") { + var argsObj = jsonNode["params"]?["arguments"]; + var filePath = argsObj?["file_path"]?.ToString(); + var outputPath = argsObj?["output_path"]?.ToString(); + var extraOptions = argsObj?["options"]?.ToString(); + + var result = ExecuteDeobfuscation(filePath, outputPath, extraOptions); + SendResponse(writer, id, result); + } else { + SendError(writer, id, -32601, $"Tool '{toolName}' not found."); + } + } + else if (id != null) { + SendError(writer, id, -32601, $"Method '{method}' not found."); + } + } + catch (Exception ex) { + SendError(writer, null, -32603, ex.Message); + } + } + } + + static void SendResponse(TextWriter writer, string id, object result) { + var response = new { + jsonrpc = "2.0", + id = id != null ? JsonValue.Create(id) : null, + result = result + }; + var json = JsonSerializer.Serialize(response); + writer.WriteLine(json); + writer.Flush(); + } + + static void SendError(TextWriter writer, string id, int code, string message) { + var response = new { + jsonrpc = "2.0", + id = id != null ? JsonValue.Create(id) : null, + error = new { + code = code, + message = message + } + }; + var json = JsonSerializer.Serialize(response); + writer.WriteLine(json); + writer.Flush(); + } + + static object ExecuteDeobfuscation(string filePath, string outputPath, string extraOptions) { + if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { + return new { + content = new[] { + new { + type = "text", + text = $"Error: File '{filePath}' does not exist or is not accessible." + } + }, + isError = true + }; + } + + var argsList = new List { filePath }; + if (!string.IsNullOrEmpty(outputPath)) { + argsList.Add("-o"); + argsList.Add(outputPath); + } + + if (!string.IsNullOrEmpty(extraOptions)) { + var tokens = extraOptions.Split(' ', StringSplitOptions.RemoveEmptyEntries); + argsList.AddRange(tokens); + } + + var originalOut = Console.Out; + var originalError = Console.Error; + + using (var sw = new StringWriter()) { + Console.SetOut(sw); + Console.SetError(sw); + + int exitCode = 1; + try { + exitCode = de4dot.cui.Program.Main(argsList.ToArray()); + } + catch (Exception ex) { + sw.WriteLine(); + sw.WriteLine("Exception caught during deobfuscation execution:"); + sw.WriteLine(ex.ToString()); + } + + Console.SetOut(originalOut); + Console.SetError(originalError); + + string outputLog = sw.ToString(); + + return new { + content = new[] { + new { + type = "text", + text = $"Execution completed with exit code: {exitCode}\n\nConsole Log:\n{outputLog}" + } + }, + isError = exitCode != 0 + }; + } + } + #endregion + } +} diff --git a/de4dot.mcp/de4dot.mcp.csproj b/de4dot.mcp/de4dot.mcp.csproj new file mode 100644 index 00000000..98a14868 --- /dev/null +++ b/de4dot.mcp/de4dot.mcp.csproj @@ -0,0 +1,15 @@ + + + + + + Exe + + net8.0 + + + + + + + diff --git a/de4dot.netcore.sln b/de4dot.netcore.sln index 92a9eb36..9c6e0fc8 100644 --- a/de4dot.netcore.sln +++ b/de4dot.netcore.sln @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "de4dot.cui", "de4dot.cui\de EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "de4dot.mdecrypt", "de4dot.mdecrypt\de4dot.mdecrypt.csproj", "{5C93C5E2-196F-4877-BF65-96FEBFCEFCA1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "de4dot.mcp", "de4dot.mcp\de4dot.mcp.csproj", "{8F940F69-122B-4EFD-A7F2-4424A325CEE1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -135,6 +137,18 @@ Global {5C93C5E2-196F-4877-BF65-96FEBFCEFCA1}.Release|Mixed Platforms.Build.0 = Release|Any CPU {5C93C5E2-196F-4877-BF65-96FEBFCEFCA1}.Release|Win32.ActiveCfg = Release|Any CPU {5C93C5E2-196F-4877-BF65-96FEBFCEFCA1}.Release|x86.ActiveCfg = Release|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Debug|Win32.ActiveCfg = Debug|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Release|Any CPU.Build.0 = Release|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Release|Win32.ActiveCfg = Release|Any CPU + {8F940F69-122B-4EFD-A7F2-4424A325CEE1}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/dotnet-deobfuscate/SKILL.md b/dotnet-deobfuscate/SKILL.md new file mode 100644 index 00000000..a2954e24 --- /dev/null +++ b/dotnet-deobfuscate/SKILL.md @@ -0,0 +1,84 @@ +--- +name: dotnet-deobfuscate +description: Deobfuscate, unpack, and analyze protected .NET assemblies (EXE and DLL) using de4dotEx. Use when an AI agent needs to analyze or reverse-engineer a .NET binary that is packed, renamed, or has encrypted strings/control flow. +--- + +# .NET Deobfuscation with de4dotEx + +This skill guides you through deobfuscating, unpacking, and cleaning protected `.NET` executables (`.exe`) and libraries (`.dll`) using `de4dotEx`. + +--- + +## Static Analysis Mapping (Detect It Easy) + +When triaging files using static analysis tools like **Detect It Easy (DiE)**, map the detections to `de4dotEx` execution as follows: + +| Detect It Easy (DiE) Detection | Obfuscator / Protector | de4dotEx Command / Options | +| :--- | :--- | :--- | +| **Protector: Confuser (1.X)** | ConfuserEx (1.X) | `de4dot ` (Auto-detect) or force: `-p crx` | +| **Protector: Babel.NET** | Babel.NET | `de4dot ` or force: `-p bl` | +| **Protector: SmartAssembly** | SmartAssembly | `de4dot ` or force: `-p sa` | +| **Protector: dotNET Reactor** | .NET Reactor | `de4dot ` or force: `-p dr4` | +| **Protector: ILProtector** | ILProtector | `de4dot ` or force: `-p il` *(Requires Strategy B Wine Container)* | +| **Protector: Agile.NET** | Agile.NET | `de4dot ` or force: `-p an` *(Requires Strategy B Wine Container)* | + +--- + +## de4dotEx CLI / API Usage + +Always try **Auto-detection** first, as de4dotEx parses assembly metadata and `ConfusedByAttribute` attributes to automatically load the correct unpacker: + +### 1. Basic Auto-Detection (Standard Run) +```bash +de4dot MyAssembly.dll -o CleanAssembly.dll +``` + +### 2. Forcing a Specific Deobfuscator (`-p` flag) +If auto-detection fails or you want to override: +* **ConfuserEx:** `de4dot -p crx MyAssembly.dll` +* **.NET Reactor:** `de4dot -p dr4 MyAssembly.dll` +* **Babel.NET:** `de4dot -p bl MyAssembly.dll` +* **SmartAssembly:** `de4dot -p sa MyAssembly.dll` + +### 3. Decrypting Strings (`-str` option) +If the obfuscator uses dynamic delegates or emulation for string decryption: +* **Emulation (Recommended):** `de4dot -str emulate MyAssembly.dll` +* **Delegates:** `de4dot -str delegate MyAssembly.dll` +* **Static:** `de4dot -str static MyAssembly.dll` + +### 4. Renaming & Symbol Restoration +* **Disable Renaming:** If symbol renaming breaks references, disable it: `--rename-symbols false` +* **Force Renaming Schema:** Force a specific renaming style: `--rename-symbols true` +* **Preserve Metadata Tokens:** For seamless decompilation alignment: `--preserve-tokens` + +--- + +## Execution Environments + +### Mode A: Native .NET 8.0 Docker Container (Static / Fast) +Runs natively at peak speed. Fully supports ConfuserEx 1.X, Babel, and SmartAssembly. +```bash +docker run --rm -v "$(pwd):/work" -w /work de4dotex -o +``` + +### Mode B: Wine .NET 4.8 Container (JIT-hook / Dynamic) +Required for protectors using native Windows memory APIs (ILProtector, Agile.NET). +```bash +# On Intel/AMD hosts: +docker run --rm -v "$(pwd):/work" de4dotex-wine -o + +# On ARM64 Apple Silicon Mac hosts (Forces Rosetta 2 x86 translation): +docker run --platform linux/amd64 --rm -v "$(pwd):/work" de4dotex-wine -o +``` + +### Mode C: Native C# Stdio MCP Server (AI Agent Integration) +Command for registering inside your MCP client config (`claude_desktop_config.json`): +```json +"mcpServers": { + "de4dotex": { + "command": "dotnet", + "args": ["/absolute/path/to/publish-net8.0-mcp/de4dot.mcp.dll"] + } +} +``` +Exposes the **`deobfuscate`** tool with parameters: `file_path`, `output_path`, and `options` (for passing custom flags like `-str delegate`). diff --git a/test_api.sh b/test_api.sh new file mode 100755 index 00000000..75ee3084 --- /dev/null +++ b/test_api.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# Exit on error +set -e + +# Help / Usage check +if [ $# -lt 1 ]; then + echo "==========================================================================" + echo " de4dotEx HTTP Web API Test Client" + echo "==========================================================================" + echo "Usage: $0 [optional_extra_flags]" + echo "" + echo "Examples:" + echo " $0 MyProtectedApp.dll" + echo " $0 /path/to/Target.exe \"--preserve-tokens -str delegate\"" + echo "==========================================================================" + exit 1 +fi + +INPUT_FILE="$1" +EXTRA_OPTIONS="$2" + +# Verify input file exists +if [ ! -f "$INPUT_FILE" ]; then + echo "Error: File '$INPUT_FILE' not found." + exit 1 +fi + +# Calculate directory and output filename (appending _cleaned before the extension) +DIR=$(dirname "$INPUT_FILE") +FILENAME=$(basename "$INPUT_FILE") +EXTENSION="${FILENAME##*.}" +BASENAME="${FILENAME%.*}" + +# Handle files with no extensions +if [ "$BASENAME" = "$EXTENSION" ]; then + OUTPUT_FILE="${DIR}/${BASENAME}_cleaned" +else + OUTPUT_FILE="${DIR}/${BASENAME}_cleaned.${EXTENSION}" +fi + +echo "[de4dotEx] Uploading '$INPUT_FILE' to http://localhost:8080/deobfuscate..." +if [ -n "$EXTRA_OPTIONS" ]; then + echo "[de4dotEx] Applying native flags: $EXTRA_OPTIONS" +fi + +# Perform HTTP POST request and capture HTTP status code +HTTP_CODE=$(curl -s -w "%{http_code}" \ + -F "file=@${INPUT_FILE}" \ + -F "options=${EXTRA_OPTIONS}" \ + http://localhost:8080/deobfuscate \ + -o "${OUTPUT_FILE}") + +if [ "$HTTP_CODE" -eq 200 ]; then + echo "------------------------------------------------------------" + echo "🎉 Success! Cleaned assembly saved to:" + echo " $OUTPUT_FILE" + echo "------------------------------------------------------------" +else + echo "------------------------------------------------------------" + echo "❌ Error: Deobfuscation failed (HTTP status: ${HTTP_CODE})." + + # If server returned an error payload, display it and clean up the file + if [ -f "${OUTPUT_FILE}" ]; then + echo "Server message:" + cat "${OUTPUT_FILE}" + echo "" + rm -f "${OUTPUT_FILE}" + fi + echo "------------------------------------------------------------" + exit 1 +fi