打工人哪里有不疯的,阴暗的尖叫扭曲爬行。
上一篇简单讲了PELoader加载器的实现,本篇文章简单分析一下NimShellCodeLoader,以及构成的技术。
NimShellCodeLoader是由Nim编写的Windows平台下的shellcode免杀加载器,据作者在github上的介绍:
1.自带四种加载方式
2.可自行拓展加载方式
3.支持两种加密技术,分别为3des加密和凯撒密码,密钥随机,每次生成文件拥有不同hash
项目地址:
https://github.com/aeverj/NimShellCodeLoader
下载到本地看看,看起来近期作者有更新,于是下个最新版本的看看
解压缩:
具体用法自己看github
这一个自动化加载器的结构,是以一个主程序(codeLoader.exe)调用shellcode加载器模板文件(CertEnumSystemStore.nim),填充用户输入的shellcode(我们用的calc.bin),执行加密shellcode,然后编译生成文件。
随机打开一个加载器模板文件,看一下代码:
![红队免杀系列之自动化Loader解读(三) 红队免杀系列之自动化Loader解读(三)]()
内容很简单,就是一个回调函数加载shellcode的方式,只不过使用了内联编译的方式,以nim来编译C++语言代码,大体流程如下:
-
{.passL:"-l Crypt32".}: 这是 Nim 的 pragma 指令,用于指示 Nim 编译器传递 -l Crypt32 选项给底层的 C/C++ 编译器,以链接 Crypt32 库。
-
int CertEnumSystem(char *shellcode, SIZE_T shellcodeSize) { ... }: 这是一个 C/C++ 函数的定义,它的主要功能是将 shellcode 复制到一个可执行内存页面(VirtualAlloc 分配的),然后将控制流传递到 shellcode 中。
-
proc CertEnumSystem(plainBuffer: cstring, size: cint): cint {.importcpp:"CertEnumSystem(@)", nodecl.}: 这是 Nim 中的一个外部函数(external procedure),它导入了上面定义的 C/C++ 函数 CertEnumSystem,允许 Nim 代码调用它。
-
discard CertEnumSystem(code, codelen): 这一行代码实际上调用了 CertEnumSystem 函数,将 code(shellcode)和 codelen(shellcode 的长度)传递给它。这将触发执行 shellcode 的操作。
这一部分是函数模板代码,外层还嵌套了一次加密(两种可供选择的加密方式):
![红队免杀系列之自动化Loader解读(三) 红队免杀系列之自动化Loader解读(三)]()
这里有两种加密方式:
1) 凯撒加密:
![红队免杀系列之自动化Loader解读(三) 红队免杀系列之自动化Loader解读(三)]()
代码解读:
-
# [...compile...]: 这个注释提供了编译该 Nim 程序的命令示例,指定了编译选项。
-
import sequtils: 导入 Nim 的 sequtils 模块,用于处理序列和集合。
-
import random, os: 导入 random 和 os 模块,用于随机数生成和文件读取。
-
import strutils: 导入 strutils 模块,用于字符串处理。
-
var dict = toSeq(0..255).mapIt(it.uint8): 创建一个名为 dict 的序列,其中包含了 0 到 255 的整数,代表字符的 ASCII 值。然后使用 mapIt 函数将这些整数转换为 uint8 类型的值。
-
randomize(): 初始化随机数生成器。
-
dict.shuffle(): 随机打乱 dict 序列中的元素,以创建一个随机的字符映射。
-
let entireFile = readFile(paramStr(1)).mapIt(it.uint8): 读取命令行参数中指定的文件,并将文件内容作为 uint8 类型的序列存储在 entireFile 变量中。
-
var finallTable = newSeq[uint8](entireFile.len): 创建一个名为 finallTable 的新序列,用于存储加密后的文件内容。
-
下面的两个嵌套循环用于将文件内容的每个字节与字符映射进行比较,然后将加密后的结果存储在 finallTable 中。
-
最后的循环将字符映射和加密后的结果连接在一起,并将其以十六进制形式输出到标准输出。
这段代码实际上实现了一个简单的凯撒密码加密,通过随机化字符映射,将文件内容中的每个字符替换为不同的字符,从而实现了加密。
2) 3DES加密:
代码解读:
-
{.compile: "des.c".}: 这个指令告诉 Nim 编译器在编译时包含 des.c 文件,这个文件包含了 D3DES 算法的实现。
-
proc D3DES_Encrypt(plainBuffer: cstring, keyBuffer: cstring, cipherBuffer: cstring, n: cint): cint {.importc, cdecl.}: 这是一个 Nim 中定义的外部函数,它导入了 D3DES 加密函数的 C 实现。它的目的是将传入的 plainBuffer 使用 keyBuffer 加密,并将结果存储在 cipherBuffer 中。
-
randomize(): 这个函数用于初始化随机数生成器,以便后续生成密钥。
-
for i in 0..23: key.add((rand(254)+1).char): 这个循环用于生成随机密钥,将每个随机数转换为字符并添加到密钥字符串中。
-
let entireFile = cast[string](readFile(paramStr(1))): 这一行代码从命令行参数中获取文件名,并读取文件内容,将其存储在 entireFile 变量中。
-
let plainBuffer: cstring = entireFile: 将文件内容转换为 C 字符串,以便传递给 D3DES_Encrypt 函数。
-
let out_len = ((entireFile.len / 8 + 1).int*8): 计算出加密后的输出长度,确保是 8 的倍数。
-
var cipherBuffer = cast[cstring](alloc0(out_len)): 分配用于存储加密结果的缓冲区。
-
discard D3DES_Encrypt(plainBuffer, key.string, cipherBuffer, cast[cint](entireFile.len)): 调用 D3DES_Encrypt 函数,对文件内容进行加密,并将结果存储在 cipherBuffer 中。
-
后续代码将加密结果转换为十六进制字符串,并将其输出到标准输出。
这段 Nim 代码用于使用 3DES 算法对给定文件内容进行加密,然后以十六进制字符串的形式将加密结果输出到标准输出。
加密密钥是随机生成的,并且作为参数传递给程序。请注意 3DES 算法通常用于保护数据的机密性,但在实际应用中,加密和解密需要使用相同的密钥,因此确保密钥的安全性非常重要 。
这一个自动化加载器的结构,是以一个主程序(codeLoader.exe)调用shellcode加载器模板文件(CertEnumSystemStore.nim),填充用户输入的shellcode(我们用的calc.bin),执行加密shellcode,然后编译生成文件。
随机打开一个加载器模板文件,看一下代码:
内容很简单,就是一个回调函数加载shellcode的方式,只不过使用了内联编译的方式,以nim来编译C++语言代码,大体流程如下:
-
{.passL:"-l Crypt32".}: 这是 Nim 的 pragma 指令,用于指示 Nim 编译器传递 -l Crypt32 选项给底层的 C/C++ 编译器,以链接 Crypt32 库。
-
int CertEnumSystem(char *shellcode, SIZE_T shellcodeSize) { ... }: 这是一个 C/C++ 函数的定义,它的主要功能是将 shellcode 复制到一个可执行内存页面(VirtualAlloc 分配的),然后将控制流传递到 shellcode 中。
-
proc CertEnumSystem(plainBuffer: cstring, size: cint): cint {.importcpp:"CertEnumSystem(@)", nodecl.}: 这是 Nim 中的一个外部函数(external procedure),它导入了上面定义的 C/C++ 函数 CertEnumSystem,允许 Nim 代码调用它。
-
discard CertEnumSystem(code, codelen): 这一行代码实际上调用了 CertEnumSystem 函数,将 code(shellcode)和 codelen(shellcode 的长度)传递给它。这将触发执行 shellcode 的操作。
这一部分是函数模板代码,外层还嵌套了一次加密(两种可供选择的加密方式):
这里有两种加密方式:
1) 凯撒加密:
代码解读:
-
# [...compile...]: 这个注释提供了编译该 Nim 程序的命令示例,指定了编译选项。
-
import sequtils: 导入 Nim 的 sequtils 模块,用于处理序列和集合。
-
import random, os: 导入 random 和 os 模块,用于随机数生成和文件读取。
-
import strutils: 导入 strutils 模块,用于字符串处理。
-
var dict = toSeq(0..255).mapIt(it.uint8): 创建一个名为 dict 的序列,其中包含了 0 到 255 的整数,代表字符的 ASCII 值。然后使用 mapIt 函数将这些整数转换为 uint8 类型的值。
-
randomize(): 初始化随机数生成器。
-
dict.shuffle(): 随机打乱 dict 序列中的元素,以创建一个随机的字符映射。
-
let entireFile = readFile(paramStr(1)).mapIt(it.uint8): 读取命令行参数中指定的文件,并将文件内容作为 uint8 类型的序列存储在 entireFile 变量中。
-
var finallTable = newSeq[uint8](entireFile.len): 创建一个名为 finallTable 的新序列,用于存储加密后的文件内容。
-
下面的两个嵌套循环用于将文件内容的每个字节与字符映射进行比较,然后将加密后的结果存储在 finallTable 中。
-
最后的循环将字符映射和加密后的结果连接在一起,并将其以十六进制形式输出到标准输出。
这段代码实际上实现了一个简单的凯撒密码加密,通过随机化字符映射,将文件内容中的每个字符替换为不同的字符,从而实现了加密。
2) 3DES加密:
代码解读:
-
{.compile: "des.c".}: 这个指令告诉 Nim 编译器在编译时包含 des.c 文件,这个文件包含了 D3DES 算法的实现。
-
proc D3DES_Encrypt(plainBuffer: cstring, keyBuffer: cstring, cipherBuffer: cstring, n: cint): cint {.importc, cdecl.}: 这是一个 Nim 中定义的外部函数,它导入了 D3DES 加密函数的 C 实现。它的目的是将传入的 plainBuffer 使用 keyBuffer 加密,并将结果存储在 cipherBuffer 中。
-
randomize(): 这个函数用于初始化随机数生成器,以便后续生成密钥。
-
for i in 0..23: key.add((rand(254)+1).char): 这个循环用于生成随机密钥,将每个随机数转换为字符并添加到密钥字符串中。
-
let entireFile = cast[string](readFile(paramStr(1))): 这一行代码从命令行参数中获取文件名,并读取文件内容,将其存储在 entireFile 变量中。
-
let plainBuffer: cstring = entireFile: 将文件内容转换为 C 字符串,以便传递给 D3DES_Encrypt 函数。
-
let out_len = ((entireFile.len / 8 + 1).int*8): 计算出加密后的输出长度,确保是 8 的倍数。
-
var cipherBuffer = cast[cstring](alloc0(out_len)): 分配用于存储加密结果的缓冲区。
-
discard D3DES_Encrypt(plainBuffer, key.string, cipherBuffer, cast[cint](entireFile.len)): 调用 D3DES_Encrypt 函数,对文件内容进行加密,并将结果存储在 cipherBuffer 中。
-
后续代码将加密结果转换为十六进制字符串,并将其输出到标准输出。
这段 Nim 代码用于使用 3DES 算法对给定文件内容进行加密,然后以十六进制字符串的形式将加密结果输出到标准输出。
加密密钥是随机生成的,并且作为参数传递给程序。请注意 3DES 算法通常用于保护数据的机密性,但在实际应用中,加密和解密需要使用相同的密钥,因此确保密钥的安全性非常重要 。
看看实际效果如何,我这里以一个calc的shellcode举例:
拖到工具里面,点击生成,生成的文件在bin目录下,点击运行试试(弹出计算器证明可以正常运行)
这一个工具呢,可以简单的理解成就像是一个洋葱的皮,包裹着真实的恶意载荷,也就是我们的shellcode。
实际上shellcode加载到内存的时候会还原展开原始的shellcode,像calc这种瞬时执行的操作问题不大,但是对于实际红队中利用,恶意载荷会很长一段时间待在内存里面,针对内存的特征扫描很容易就识别这些公开的shellcode特征,所以也就糊弄糊弄一般杀软还行。
微步在线验证
1)新版本测试
这里我选择的是作者最近一段时间更新的版本,看起来精简了一些东西,优化了免杀的效果:
2)旧版本的测试
和上面的加载shellcode是一致的,只不过微步识别是恶意
总体还算可以,这一个加载器主打一个简单便捷,没有太多花里胡哨的操作(
自己研究点骚操作,加进去),适合借鉴思路,作为参考自己的武器化实现。
还有,网安穷三代,奉劝后来人!!!
原文始发于微信公众号(JC的安全之路):红队免杀系列之自动化Loader解读(三)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论