原始仓库的 Nim 移植版:https://github.com/mgeeky/ShellcodeFluctuation 所有注释都按原样从原始仓库复制粘贴而来,因为它们确实帮助我理解了这项技术。关于该技术本身的更多信息,我强烈建议阅读原始仓库的 README!
安装依赖:
nimble install winim ptr_math
我完全清楚,这种内存加密技术在 OpSec 方面并不是最好的。它只会精确加密那些为其定义的内存区域。因此,例如对于 C2 植入体,所有新执行的模块内存地址在 Sleep 时不会被加密。对于基于堆的检测(例如针对 Cobalt-Strike 配置的检测),你仍然需要在此处添加堆加密。
但尽管如此,内存加密技术仍然可以完美地用于避免自动内存扫描器在 Sleep 时对主C2 植入体的检测。我已经成功使用 Covenant 和一个自定义的无状态 Payload 像这样 测试了该 PoC。
在这个移植版中,加密/解密密钥以及 Shellcode 地址和更多值(如保护属性(RW))必须手动定义。在原始仓库中,Shellcode 调用者地址是自动获取的。
Example.nim 文件包含了你入门所需的一切。当然,当使用 C2-Shellcode 时,你不需要自己调用 Sleep(这只是为了触发 PoC 的一切),那应该由你的 C2 本身来完成。目前这仅兼容使用 Win32 Sleep 函数的 C2 框架,但如果你将 Hook 修改为另一个函数,它也会以同样的方式工作。
import winim
import Fluctuation
echo "Trying to hook Sleep"
if (hookSleep()):
echo "Hooked Sleep successfully!"
type
PocArray = array[12, byte]
let names: PocArray = [byte 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA]
g_fluctuationData.shellcodeAddr = unsafeAddr names[0]
echo "Shellcode address:"
echo repr(g_fluctuationData.shellcodeAddr)
g_fluctuationData.shellcodeSize = size_t(len(names))
echo "Shellcode Size:"
echo repr(g_fluctuationData.shellcodeSize)
when defined(amd64):
g_fluctuationData.encodeKey = 0xDEADB33f
when defined(i386):
g_fluctuationData.encodeKey = 0xDEAD
g_fluctuationData.currentlyEncrypted = false
g_fluctuationData.protect = PAGE_READWRITE
g_fluctuate = FluctuateToRW
echo "Calling Sleep"
Sleep(2500)
echo "Everything went fine and as expected"
else:
echo "Failed to hook Sleep"
quit(1)
测试加密 PoC 可以这样进行,例如:
nim c --debuginfo --linedir:on Example.nim
使用调试器启动:
gdb Example.exe
在 Sleep 之前和之后设置断点:
# Before encryption
break Example.nim:31
# After encryption
break Fluctuation.nim:98
# Original value restored
break Example.nim:33
之后像这样检查每个断点处 Shellcode 地址的值:

波动到 NA 尚不能工作,因为在测试中 Vectored Exception Handler 从未被触发,而应用程序崩溃了。将来某个时候我不得不做进一步的测试/故障排除。欢迎你自己测试!:-)