split nuitka flow by dll

上次透過 ollvm 混淆,我發現這個網站感覺同個類型的不斷上傳會導致我的樣本數越檢測越多,這次再來透過一個方法來躲避看看,根據Nuitka的流程是 透過兩階段才能執行我的 binary 假設透過 --onefile 和 --standalone 壓出來exe被檢測的機率應該是蠻高的很大機率被識別為特洛伊木馬或者 Nuitka package 等等那其實 那假設希望下 --onefile又不想要核心主程式被刪掉或檢測 應該可以嘗試我的方法 目前實測應該都ok 那麼說一下nuitka 的 --onefile的行為好了,假設這樣編譯的話 nuitka最終會產生一個 執行檔,而 --standalone 一起下的會 這個執行檔執行的時候會解壓出相對應的 lib 相關文件等等,就是這個行為會導致被一堆防毒軟體檢測為有病毒,那我最後用的方法嘗試了兩個,第一我幫 nuitka 的 kernel.exe 壓縮進去 dll 檔案 然後再透過 另一個程式碼呼叫 這個dll 進行動態解壓縮,然後透過windows api 直接 load到記憶體中執行,這行為差不多就會被掃毒網站掃到一堆,另一個方法就是解壓縮到另一個目錄然後再執行,這樣的話就不會發生執行和解壓縮行為就會寫到dll ,而呼叫這個dll的程式碼也基本上不會被檢測到,

compressor

code
gcc -c cm.c -o compressor

這個compressor會將elf 壓成 xxx.c 你的程式碼會變成一個char 陣列

code
compressor <target file> <outputfile>
compressor test.exe embedded.c
code
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("Usage: %s <input_file> <output_c_file>\n", argv[0]);
        return 1;
    }

    const char *input_file = argv[1];
    const char *output_c_file = argv[2];
    FILE *in = fopen(input_file, "rb");
    if (!in) {
        perror("Failed to open input file");
        return 1;
    }
    fseek(in, 0, SEEK_END);
    size_t input_size = ftell(in);
    fseek(in, 0, SEEK_SET);

    unsigned char *input_data = malloc(input_size);
    fread(input_data, 1, input_size, in);
    fclose(in);

    size_t compressed_size = compressBound(input_size);
    unsigned char *compressed_data = malloc(compressed_size);

    if (compress(compressed_data, &compressed_size, input_data, input_size) != Z_OK) {
        printf("Compression failed.\n");
        free(input_data);
        free(compressed_data);
        return 1;
    }

    free(input_data);

    FILE *out = fopen(output_c_file, "w");
    if (!out) {
        perror("Failed to create output C file");
        free(compressed_data);
        return 1;
    }

    fprintf(out, "#include <stddef.h>\n\n");
    fprintf(out, "const unsigned char compressed_data[] = {");
    for (size_t i = 0; i < compressed_size; i++) {
        if (i % 16 == 0) fprintf(out, "\n    ");
        fprintf(out, "0x%02x, ", compressed_data[i]);
    }
    fprintf(out, "\n};\n");
    fprintf(out, "const size_t compressed_size = %zu;\n", compressed_size);

    fclose(out);
    free(compressed_data);

    printf("Generated embedded C file: %s\n", output_c_file);
    return 0;
}

dll.c

而這邊透過 zlib 進行編譯 記得要加-lz

code
gcc -shared -o loader.dll dll.c embedded.c -lz

你會得到一個 loader.dll

code
#include <windows.h>
#include "zlib.h"
#include <stdlib.h>
#include <stdio.h>

// Embedded compressed data (must be defined elsewhere)
extern const unsigned char compressed_data[];
extern const size_t compressed_size;

// Target file path
const char *target_file_path = "lib\\andes_autotuning.exe"; // Change '/' to '\\' for Windows compatibility

// Decompress compressed data into memory
void *decompress_to_memory(size_t *output_size) {
    unsigned long uncompressed_size = 250 * 1024 * 1024; // Assuming max decompression size is 250MB
    unsigned char *uncompressed_data = malloc(uncompressed_size);
    if (!uncompressed_data) {
        printf("Failed to allocate memory for decompression.\n");
        return NULL;
    }

    z_stream strm = {0};
    strm.next_in = (unsigned char *)compressed_data;
    strm.avail_in = compressed_size;
    strm.next_out = uncompressed_data;
    strm.avail_out = uncompressed_size;

    if (inflateInit(&strm) != Z_OK) {
        printf("Failed to initialize zlib for decompression.\n");
        free(uncompressed_data);
        return NULL;
    }

    int ret = inflate(&strm, Z_FINISH);
    if (ret != Z_STREAM_END) {
        printf("Decompression failed. Error code: %d\n", ret);
        free(uncompressed_data);
        inflateEnd(&strm);
        return NULL;
    }

    *output_size = strm.total_out;
    inflateEnd(&strm);

    return uncompressed_data;
}

// Write the decompressed binary to the target path
int write_to_target_file(const void *data, size_t size, const char *path) {
    FILE *file = fopen(path, "wb");
    if (!file) {
        printf("Failed to create target file: %s\n", path);
        return 0;
    }

    fwrite(data, 1, size, file);
    fclose(file);

    return 1;
}

void launch_binary(const char *file_path, const char *args) {
    STARTUPINFOA si = {0};
    PROCESS_INFORMATION pi = {0};
    si.cb = sizeof(si);

    // Construct the command line with multiple arguments
    char command_line[MAX_PATH + 1024];
    snprintf(command_line, sizeof(command_line), "\"%s\" %s", file_path, args);

    printf("Command Line: %s\n", command_line);

    if (!CreateProcessA(
            NULL,                // Application name
            command_line,        // Command line arguments
            NULL,                // Process security attributes
            NULL,                // Thread security attributes
            FALSE,               // Inherit handles
            0,                   // Creation flags
            NULL,                // Environment variables
            NULL,                // Current directory
            &si, &pi)) {         // Startup and process information
        printf("Failed to launch binary. Error: %lu\n", GetLastError());
        return;
    }

    printf("Binary launched successfully with arguments: %s\n", args);

    // Wait for the process to complete
    WaitForSingleObject(pi.hProcess, INFINITE);

    // Clean up handles
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

__declspec(dllexport) void RunBinary(const char *args) {
    printf("Received arguments: %s\n", args);

    size_t uncompressed_size;
    void *uncompressed_data = decompress_to_memory(&uncompressed_size);
    if (!uncompressed_data) {
        printf("Decompression failed.\n");
        return;
    }

    if (!write_to_target_file(uncompressed_data, uncompressed_size, target_file_path)) {
        printf("Failed to write decompressed file to target path.\n");
        free(uncompressed_data);
        return;
    }

    free(uncompressed_data);

    // Launch the binary with the arguments
    launch_binary(target_file_path, args);
}

dll runner

code
gcc -o main_program dllrun.c

這樣的話就可以透過把核心邏輯寫在 dll了,實測下來效果還不錯

code
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Update the typedef to pass multiple arguments as a single string
typedef void (*RunBinaryFn)(const char *);

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: andes_autotuning.exe --config <config_path>\n");
        return 1;
    }

    // Calculate total length of parameters
    size_t total_length = 0;
    for (int i = 1; i < argc; ++i) {
        total_length += strlen(argv[i]) + 1; // Include spaces or null terminators
    }

    // Allocate memory for the combined parameter string
    char *param = (char *)malloc(total_length);
    if (!param) {
        printf("Memory allocation failed.\n");
        return 1;
    }

    param[0] = '\0'; // Initialize the string

    // Combine all arguments into a single string
    for (int i = 1; i < argc; ++i) {
        strncat(param, argv[i], total_length - strlen(param) - 1);
        if (i < argc - 1) {
            strncat(param, " ", total_length - strlen(param) - 1); // Add a space between arguments
        }
    }

    // Load the DLL
    HMODULE hDll = LoadLibrary("loader.dll");
    if (!hDll) {
        printf("Failed to load DLL. Error: %lu\n", GetLastError());
        free(param);
        return 1;
    }

    // Get the exported function address
    RunBinaryFn runBinary = (RunBinaryFn)GetProcAddress(hDll, "RunBinary");
    if (!runBinary) {
        printf("Failed to find RunBinary function. Error: %lu\n", GetLastError());
        FreeLibrary(hDll);
        free(param);
        return 1;
    }

    // Call the DLL's function and pass the parameters
    printf("Calling RunBinary from DLL with parameters: %s\n", param);
    runBinary(param);

    // Unload the DLL and free resources
    FreeLibrary(hDll);
    free(param);

    return 0;
}

load memory

這邊就是直接load 到memory 了不過沒什麼用哈哈

code
#include <windows.h>
#include <stdio.h>
#include <zlib.h>
#include <stdlib.h>

// 嵌入的压缩数据
extern const unsigned char compressed_data[];
extern const size_t compressed_size;

// 解压缩数据到内存
void *decompress_to_memory(size_t *output_size) {
    unsigned long uncompressed_size = 10 * 1024 * 1024; // 假设最大解压大小为 10MB
    unsigned char *uncompressed_data = malloc(uncompressed_size);
    if (!uncompressed_data) {
        printf("Failed to allocate memory for decompression.\n");
        return NULL;
    }

    z_stream strm = {0};
    strm.next_in = (unsigned char *)compressed_data;
    strm.avail_in = compressed_size;
    strm.next_out = uncompressed_data;
    strm.avail_out = uncompressed_size;

    if (inflateInit(&strm) != Z_OK) {
        printf("Failed to initialize zlib for decompression.\n");
        free(uncompressed_data);
        return NULL;
    }

    int ret = inflate(&strm, Z_FINISH);
    if (ret != Z_STREAM_END) {
        printf("Decompression failed. Error code: %d\n", ret);
        free(uncompressed_data);
        inflateEnd(&strm);
        return NULL;
    }

    *output_size = strm.total_out;
    inflateEnd(&strm);

    return uncompressed_data;
}

// 在内存中加载并运行 PE 文件
void load_and_run_pe(const void *pe_data) {
    IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER *)pe_data;
    IMAGE_NT_HEADERS *nt_headers = (IMAGE_NT_HEADERS *)((BYTE *)pe_data + dos_header->e_lfanew);

    void *base_address = VirtualAlloc(NULL,
                                      nt_headers->OptionalHeader.SizeOfImage,
                                      MEM_RESERVE | MEM_COMMIT,
                                      PAGE_EXECUTE_READWRITE);
    if (!base_address) {
        printf("Failed to allocate memory for PE file.\n");
        return;
    }

    memcpy(base_address, pe_data, nt_headers->OptionalHeader.SizeOfHeaders);

    IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nt_headers);
    for (int i = 0; i < nt_headers->FileHeader.NumberOfSections; i++) {
        void *dest = (BYTE *)base_address + section[i].VirtualAddress;
        void *src = (BYTE *)pe_data + section[i].PointerToRawData;
        memcpy(dest, src, section[i].SizeOfRawData);
    }

    void (*entry_point)() = (void (*)())((BYTE *)base_address + nt_headers->OptionalHeader.AddressOfEntryPoint);
    printf("Jumping to entry point...\n");
    entry_point();

    VirtualFree(base_address, 0, MEM_RELEASE);
}

int main() {
    size_t uncompressed_size;

    void *uncompressed_data = decompress_to_memory(&uncompressed_size);
    if (!uncompressed_data) {
        printf("Decompression failed.\n");
        return 1;
    }

    load_and_run_pe(uncompressed_data);
    free(uncompressed_data);

    return 0;
}

image image