├── .gitignore ├── mail.ico ├── screenshot └── main_eng.png ├── locale └── zh_CN │ └── LC_MESSAGES │ ├── lang_zh_CN.mo │ └── lang_zh_CN.po ├── .idea ├── vcs.xml ├── .gitignore ├── inspectionProfiles │ └── profiles_settings.xml ├── modules.xml ├── misc.xml └── SampleMailSubmitter.iml ├── make.bat ├── README.md ├── LICENSE.md ├── Sender.py ├── GUI.py ├── Main.py └── GUI.fbp /.gitignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /build/ 3 | -------------------------------------------------------------------------------- /mail.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryLinLinLin/SampleMailSubmitter/HEAD/mail.ico -------------------------------------------------------------------------------- /screenshot/main_eng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryLinLinLin/SampleMailSubmitter/HEAD/screenshot/main_eng.png -------------------------------------------------------------------------------- /locale/zh_CN/LC_MESSAGES/lang_zh_CN.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryLinLinLin/SampleMailSubmitter/HEAD/locale/zh_CN/LC_MESSAGES/lang_zh_CN.mo -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | pyinstaller --noconfirm --log-level=WARN ^ 2 | -F -w -n "SampleMailSubmitter" ^ 3 | --add-data="locale;locale" ^ 4 | --add-data="mail.ico;." ^ 5 | --icon="mail.ico" ^ 6 | --upx-exclude=vcruntime140.dll ^ 7 | Main.py -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/SampleMailSubmitter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SampleMailSubmitter 2 | A tool for automatically sending suspicious/false-flag files via email to Antivirus vendors 3 | ## Screenshot 4 | 5 | 6 | ## Features 7 | 8 | - Automatically compress all files into a single encrypted zip. 9 | - Customizable Zip password and Email content. 10 | - Add/Remove items from Antivirus vendor list 11 | - Automatically save login info 12 | - Send with one click 13 | - Multi-language support (English or Chinese Simplified) 14 | 15 | ## How to use 16 | 17 | 1. Drag files you want to send to the text window. 18 | 2. Input your email info, SMTP and port info. 19 | 3. Select the Antivirus vendors you want to send. 20 | 4. Click Submit button and done. 21 | 22 | ## Licence 23 | 24 | See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT). 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2021] [JerryLinLinLin] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /locale/zh_CN/LC_MESSAGES/lang_zh_CN.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: \n" 4 | "POT-Creation-Date: 2020-07-28 10:22-0500\n" 5 | "PO-Revision-Date: 2020-07-28 10:23-0500\n" 6 | "Last-Translator: \n" 7 | "Language-Team: \n" 8 | "Language: zh_CN\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "X-Generator: Poedit 2.4\n" 13 | "X-Poedit-Basepath: ../../..\n" 14 | "Plural-Forms: nplurals=1; plural=0;\n" 15 | "X-Poedit-SearchPath-0: GUI.py\n" 16 | "X-Poedit-SearchPath-1: Main.py\n" 17 | "X-Poedit-SearchPath-2: Sender.py\n" 18 | 19 | #: GUI.py:23 20 | msgid "Sample Mail Submitter" 21 | msgstr "上报可疑文件" 22 | 23 | #: GUI.py:30 24 | msgid "Choose File(s)" 25 | msgstr "选择单个/多个文件" 26 | 27 | #: GUI.py:32 Sender.py:209 28 | msgid "#Drag all file(s) here. One line per file." 29 | msgstr "#拖拽文件至此,每行单个文件。" 30 | 31 | #: GUI.py:38 32 | msgid "EMail Account" 33 | msgstr "Email 信息" 34 | 35 | #: GUI.py:42 36 | msgid "Email Address: " 37 | msgstr "邮箱地址: " 38 | 39 | #: GUI.py:50 40 | msgid "Password: " 41 | msgstr "邮箱密码: " 42 | 43 | #: GUI.py:58 44 | msgid "Remember" 45 | msgstr "记住" 46 | 47 | #: GUI.py:61 48 | msgid "SMTP Address: " 49 | msgstr "SMTP地址: " 50 | 51 | #: GUI.py:69 52 | msgid "Port: " 53 | msgstr "端口: " 54 | 55 | #: GUI.py:83 56 | msgid "Submission Type" 57 | msgstr "上报类型" 58 | 59 | #: GUI.py:87 60 | msgid "False Negative" 61 | msgstr "漏报文件" 62 | 63 | #: GUI.py:91 64 | msgid "False Positive" 65 | msgstr "误报文件" 66 | 67 | #: GUI.py:94 68 | msgid "Zip Password" 69 | msgstr "压缩密码" 70 | 71 | #: GUI.py:97 72 | msgid "Customize..." 73 | msgstr "自定义..." 74 | 75 | #: GUI.py:108 76 | msgid "Vendors" 77 | msgstr "厂商" 78 | 79 | #: GUI.py:112 80 | msgid "Selected Vendors: " 81 | msgstr "已选厂商: " 82 | 83 | #: GUI.py:117 84 | msgid "Vendor List: " 85 | msgstr "厂商列表: " 86 | 87 | #: GUI.py:128 88 | msgid "<<" 89 | msgstr "<<" 90 | 91 | #: GUI.py:131 92 | msgid ">>" 93 | msgstr ">>" 94 | 95 | #: GUI.py:134 96 | msgid "..." 97 | msgstr "..." 98 | 99 | #: GUI.py:140 100 | msgid "Avira;virus@avira.com" 101 | msgstr "" 102 | 103 | #: GUI.py:140 104 | msgid "Avira;novirus@avira.com" 105 | msgstr "" 106 | 107 | #: GUI.py:140 108 | msgid "Kaspersky;newvirus@kaspersky.com" 109 | msgstr "" 110 | 111 | #: GUI.py:140 112 | msgid "Kaspersky;apac-virussample@kaspersky.com" 113 | msgstr "" 114 | 115 | #: GUI.py:140 116 | msgid "ESET;samples@eset.sk" 117 | msgstr "" 118 | 119 | #: GUI.py:140 120 | msgid "ESET;samples@eset.com" 121 | msgstr "" 122 | 123 | #: GUI.py:140 124 | msgid "Huorong;seclab@huorong.cn" 125 | msgstr "" 126 | 127 | #: GUI.py:140 128 | msgid "BitDefender;oemsamples@bitdefender.com" 129 | msgstr "" 130 | 131 | #: GUI.py:140 132 | msgid "BitDefender;virus_submission@bitdefender.com" 133 | msgstr "" 134 | 135 | #: GUI.py:152 136 | msgid "Other Settings: " 137 | msgstr "其他设置: " 138 | 139 | #: GUI.py:154 140 | msgid "Program Language: " 141 | msgstr "程序语言: " 142 | 143 | #: GUI.py:159 144 | msgid "English" 145 | msgstr "English" 146 | 147 | #: GUI.py:159 148 | msgid "Chinese Simplified" 149 | msgstr "简体中文" 150 | 151 | #: GUI.py:169 152 | msgid "Pack All to Desktop" 153 | msgstr "打包至桌面" 154 | 155 | #: GUI.py:172 156 | msgid "Pack and Submit All" 157 | msgstr "打包+提交全部" 158 | 159 | #: GUI.py:243 160 | msgid "Custom Mail Content" 161 | msgstr "自定义邮件内容" 162 | 163 | #: GUI.py:252 164 | msgid "False Negative Text" 165 | msgstr "漏报邮件内容" 166 | 167 | #: GUI.py:257 168 | msgid "False Positive Text" 169 | msgstr "误报邮件内容" 170 | 171 | #: GUI.py:278 172 | msgid "OK" 173 | msgstr "确定" 174 | 175 | #: GUI.py:281 176 | msgid "Cancel" 177 | msgstr "取消" 178 | 179 | #: Main.py:42 180 | msgid "Enter password for compressing" 181 | msgstr "输入压缩密码" 182 | 183 | #: Main.py:43 184 | msgid "Set Zip Password" 185 | msgstr "设置压缩密码" 186 | 187 | #: Main.py:50 188 | msgid "Password Cannot be Empty" 189 | msgstr "密码不能为空" 190 | 191 | #: Main.py:51 Main.py:98 Main.py:127 Sender.py:146 Sender.py:160 Sender.py:197 192 | msgid "ERROR" 193 | msgstr "错误" 194 | 195 | #: Main.py:62 196 | msgid "Password will be stored in the local config file. Continue?" 197 | msgstr "邮箱密码将会保存到本地配置文件。是否继续?" 198 | 199 | #: Main.py:63 200 | msgid "WARNING" 201 | msgstr "警告" 202 | 203 | #: Main.py:86 204 | msgid "Use \";\" to separate name and email." 205 | msgstr "使用“;”区分厂商名称和邮件地址。" 206 | 207 | #: Main.py:87 208 | msgid "Edit Vendor List" 209 | msgstr "编辑厂商列表" 210 | 211 | #: Main.py:97 212 | msgid "Incorrect Data Format" 213 | msgstr "数据格式错误" 214 | 215 | #: Main.py:112 216 | msgid "Sending Samples..." 217 | msgstr "发送样本..." 218 | 219 | #: Main.py:125 Sender.py:158 220 | msgid "Cannot Access Sample File(s). Check paths.\n" 221 | msgstr "无法读取样本文件。请检查路径。\n" 222 | 223 | #: Main.py:132 224 | msgid "Pack to Desktop succeed!" 225 | msgstr "成功打包样本至桌面!" 226 | 227 | #: Main.py:133 Sender.py:206 228 | msgid "INFO" 229 | msgstr "信息" 230 | 231 | #: Main.py:187 232 | #, python-brace-format 233 | msgid "" 234 | "Hello,\n" 235 | "\n" 236 | "The attached files may contain threats. Require for the further analysis.\n" 237 | "\n" 238 | "Password: {password} \n" 239 | "\n" 240 | "Thanks! " 241 | msgstr "" 242 | 243 | #: Main.py:189 244 | #, python-brace-format 245 | msgid "" 246 | "Hello,\n" 247 | "\n" 248 | "The attached files may be clean. Require for the further analysis.\n" 249 | "\n" 250 | "Password: {password} \n" 251 | "\n" 252 | "Thanks! " 253 | msgstr "" 254 | 255 | #: Sender.py:145 256 | msgid "Missing or incorrect parameters. Check if all info filled correctly." 257 | msgstr "丢失参数或参数错误。请检查所有设置。" 258 | 259 | #: Sender.py:153 260 | msgid "Compressing Files..." 261 | msgstr "压缩文件中..." 262 | 263 | #: Sender.py:168 264 | msgid "Composing Mail..." 265 | msgstr "生成邮件中..." 266 | 267 | #: Sender.py:177 268 | msgid "Login To Your Email..." 269 | msgstr "登录邮箱中..." 270 | 271 | #: Sender.py:192 272 | msgid "Sending Email..." 273 | msgstr "发送邮件中..." 274 | 275 | #: Sender.py:195 276 | msgid "Login Fail. Check Internet connection, your login info, or other config.\n" 277 | msgstr "登录邮箱失败。请检查网络连接,登录信息,或其他设置\n" 278 | 279 | #: Sender.py:204 280 | msgid "SUCCEED!" 281 | msgstr "成功!" 282 | 283 | #: Sender.py:205 284 | msgid "Email Sent. You may login your email to check the status." 285 | msgstr "邮件已发送,请登录查看发送状态。" 286 | 287 | #~ msgid "Cannot Access Sample File(s). Check paths." 288 | #~ msgstr "无法读取样本文件。请检查路径。" 289 | 290 | #, python-brace-format 291 | #~ msgid "" 292 | #~ "Cannot Access Sample File(s). Check paths.\n" 293 | #~ "Error: {error}\n" 294 | #~ "Info: {info}" 295 | #~ msgstr "" 296 | #~ "无法读取样本文件。请检查路径\n" 297 | #~ "错误: {error}\n" 298 | #~ "信息: {info}" 299 | -------------------------------------------------------------------------------- /Sender.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import pyminizip 3 | import os 4 | import datetime 5 | import smtplib 6 | import ssl 7 | import Main 8 | import wx 9 | from email.mime.text import MIMEText 10 | from email.mime.multipart import MIMEMultipart 11 | from email.mime.application import MIMEApplication 12 | 13 | # for i18n support 14 | _ = wx.GetTranslation 15 | 16 | 17 | class SampleFiles: 18 | """ 19 | Class represent sample Files for compressing 20 | """ 21 | file_list: list = None # All file paths 22 | password: str = None # password for compressing 23 | output_path: str = None # output path 24 | 25 | def __init__(self, file_list: str, password: str): 26 | """ 27 | Initialize compressing 28 | :param file_list: file paths 29 | :param password: compress password 30 | """ 31 | self.file_list = file_list.split('\n') 32 | self.password = password 33 | 34 | def compress(self): 35 | """ 36 | Compress files to user dir 37 | :return: output path 38 | """ 39 | cur_time = str(datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d-%H-%M-%S')) 40 | user_path = os.getenv('APPDATA') + "\\VirusSampleSubmitter" 41 | output_path = '{user_path}\\SamplePack[{time}].zip'.format(user_path=user_path, time=cur_time) 42 | self.output_path = output_path 43 | pyminizip.compress_multiple(self.file_list, [], output_path, self.password, 8) 44 | return output_path 45 | 46 | def delete_zip(self): 47 | """Delete self after compressing""" 48 | if self.output_path is not None: 49 | os.remove(self.output_path) 50 | 51 | def compress_to_desktop(self): 52 | """ 53 | Compress files to user desktop 54 | :return: output path 55 | """ 56 | cur_time = str(datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d-%H-%M-%S')) 57 | output_path = '{desktop_path}\\SamplePack[{time}].zip'.format(desktop_path=os.path.expanduser("~\\Desktop"), 58 | time=cur_time) 59 | self.output_path = output_path 60 | pyminizip.compress_multiple(self.file_list, [], output_path, self.password, 8) 61 | return output_path 62 | 63 | 64 | class Mail: 65 | """ 66 | Class represent email body 67 | """ 68 | mail: MIMEMultipart = None # mail body 69 | mail_dst_list: list = None # vendor list 70 | 71 | def __init__(self, mail_src, mail_dst, mail_type, mail_content, attach_path): 72 | """ 73 | Initialize mail obj 74 | :param mail_src: sender 75 | :param mail_dst: receivers 76 | :param mail_type: false negative or positive 77 | :param mail_content: content 78 | :param attach_path: sample pack path 79 | """ 80 | mail = MIMEMultipart() 81 | mail['From'] = mail_src 82 | mail['To'] = ';'.join(self._extract_mails(mail_dst)) 83 | mail['Subject'] = self._get_mail_title(mail_type) 84 | mail.attach(MIMEText(mail_content, 'plain', 'utf-8')) 85 | with open(attach_path, 'rb') as f: 86 | attachment = MIMEApplication(f.read()) 87 | attachment.add_header('Content-Disposition', 'attachment', 88 | filename=str(attach_path)[str(attach_path).rfind("\\") + 1:len(attach_path)]) 89 | mail.attach(attachment) 90 | self.mail = mail 91 | self.mail_dst_list = self._extract_mails(mail_dst) 92 | return 93 | 94 | def _extract_mails(self, mails_string_array): 95 | """ 96 | Extract email address from vendor list 97 | :param mails_string_array: 98 | :return: 99 | """ 100 | result_list: list = [] 101 | for mail_string in mails_string_array: 102 | address = mail_string[mail_string.find(";") + 1: len(mail_string)] 103 | result_list.append(address) 104 | return result_list 105 | 106 | def _get_mail_title(self, mail_type): 107 | """ 108 | Get the title of mail based on type 109 | :param mail_type: fn or fp 110 | :return: 111 | """ 112 | cur_time = str(datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d-%H-%M-%S')) 113 | if mail_type == 0: 114 | return '[Malware]{time}'.format(time=cur_time) 115 | else: 116 | return '[False Positive]{time}'.format(time=cur_time) 117 | 118 | def get_mail(self): 119 | return self.mail 120 | 121 | def get_mail_dst_list(self): 122 | return self.mail_dst_list 123 | 124 | 125 | class SendingThread(threading.Thread): 126 | """ 127 | Class for sending sample file 128 | """ 129 | frame: Main.AppMainFrame = None # main frame 130 | 131 | def __init__(self, frame): 132 | """ 133 | Start thread 134 | :param frame: main frame 135 | """ 136 | threading.Thread.__init__(self) 137 | self.frame = frame 138 | self.start() 139 | 140 | def run(self): 141 | """Sending sample""" 142 | status = self.frame.progress_bar 143 | 144 | if not self._basic_check(): # check if parameters are valid 145 | wx.MessageBox(message=_('Missing or incorrect parameters. Check if all info filled correctly.'), 146 | caption=_('ERROR'), 147 | style=wx.OK | wx.ICON_ERROR) 148 | status.Destroy() 149 | return 150 | 151 | # Compressing files 152 | status.Update(value=25, 153 | newmsg=_('Compressing Files...')) 154 | sample = SampleFiles(self.frame.file_input.GetValue(), self.frame.zip_password) 155 | try: 156 | output_path = sample.compress() 157 | except Exception as e: 158 | wx.MessageBox(message=_('Cannot Access Sample File(s). Check paths.\n') + 159 | 'Error: {error}\nInfo: {info}'.format(error=e.__class__.__name__, info=str(e)), 160 | caption=_('ERROR'), 161 | style=wx.OK | wx.ICON_ERROR) 162 | sample.delete_zip() 163 | status.Destroy() 164 | return 165 | 166 | # Build email body 167 | status.Update(value=50, 168 | newmsg=_('Composing Mail...')) 169 | mail = Mail(mail_src=self.frame.email_account.GetValue(), 170 | mail_dst=self.frame.selected_vendors.GetStrings(), 171 | mail_type=self._get_mail_type(), 172 | mail_content=self._get_mail_content(), 173 | attach_path=output_path) 174 | 175 | # Login to email account and send email 176 | status.Update(value=75, 177 | newmsg=_('Login To Your Email...')) 178 | mail_body = mail.get_mail() 179 | mail_src = self.frame.email_account.GetValue() 180 | mail_password = self.frame.password_input.GetValue() 181 | mail_smtp = self.frame.smtp_input.GetValue() 182 | mail_port = self.frame.port_input.GetValue() 183 | 184 | try: 185 | context = ssl.SSLContext(ssl.PROTOCOL_TLS) # use ssl 186 | mail_main = smtplib.SMTP(host=mail_smtp, port=mail_port) 187 | mail_main.ehlo() 188 | mail_main.starttls(context=context) 189 | mail_main.ehlo() 190 | mail_main.login(user=mail_src, password=mail_password) 191 | status.Update(value=90, 192 | newmsg=_('Sending Email...')) 193 | mail_main.sendmail(from_addr=mail_src, to_addrs=mail.get_mail_dst_list(), msg=mail_body.as_string()) 194 | except Exception as e: 195 | wx.MessageBox(message=_('Login Fail. Check Internet connection, your login info, or other config.\n') + 196 | 'Error: {error}\nInfo: {info}'.format(error=e.__class__.__name__, info=str(e)), 197 | caption=_('ERROR'), 198 | style=wx.OK | wx.ICON_ERROR) 199 | status.Destroy() 200 | sample.delete_zip() 201 | return 202 | 203 | status.Update(value=100, 204 | newmsg=_('SUCCEED!')) 205 | wx.MessageBox(message=_('Email Sent. You may login your email to check the status.'), 206 | caption=_('INFO'), 207 | style=wx.OK | wx.ICON_INFORMATION) 208 | status.Destroy() 209 | self.frame.file_input.SetValue(_(u"#Drag all file(s) here. One line per file.")) 210 | sample.delete_zip() 211 | return 212 | 213 | def _get_mail_type(self): 214 | """Get mail type; 0 for fn, 1 for fp""" 215 | if self.frame.false_neg_select.GetValue(): 216 | return 0 217 | if self.frame.false_positive_select.GetValue(): 218 | return 1 219 | 220 | def _get_mail_content(self): 221 | """Get mail content based on type""" 222 | if self._get_mail_type() == 0: 223 | return self.frame.false_negative_content.format(password=self.frame.zip_password) 224 | if self._get_mail_type() == 1: 225 | return self.frame.false_positive_content.format(password=self.frame.zip_password) 226 | 227 | def _basic_check(self): 228 | """Check if parameters are valid""" 229 | f = self.frame 230 | if (f.email_account.GetValue() == '' or 231 | f.password_input.GetValue() == '' or 232 | f.smtp_input.GetValue() == '' or 233 | f.port_input.GetValue() == '' or 234 | f.zip_password == '' or 235 | f.selected_vendors.GetStrings() == []): 236 | return False 237 | return True 238 | -------------------------------------------------------------------------------- /GUI.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ########################################################################### 4 | ## Python code generated with wxFormBuilder (version Oct 26 2018) 5 | ## http://www.wxformbuilder.org/ 6 | ## 7 | ## PLEASE DO *NOT* EDIT THIS FILE! 8 | ########################################################################### 9 | 10 | import wx 11 | import wx.xrc 12 | 13 | # For i18n support 14 | _ = wx.GetTranslation 15 | 16 | ########################################################################### 17 | ## Class main_frame 18 | ########################################################################### 19 | 20 | class main_frame ( wx.Frame ): 21 | 22 | def __init__( self, parent ): 23 | wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = _(u"Sample Mail Submitter"), pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.TAB_TRAVERSAL ) 24 | 25 | self.SetSizeHints( wx.DefaultSize, wx.Size( 500,600 ) ) 26 | self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOW ) ) 27 | 28 | bSizer2 = wx.BoxSizer( wx.VERTICAL ) 29 | 30 | sbSizer1 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, _(u"Choose File(s)") ), wx.VERTICAL ) 31 | 32 | self.file_input = wx.TextCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, _(u"#Drag all file(s) here. One line per file."), wx.DefaultPosition, wx.Size( 500,100 ), wx.TE_MULTILINE|wx.HSCROLL|wx.VSCROLL ) 33 | sbSizer1.Add( self.file_input, 0, wx.ALL, 5 ) 34 | 35 | 36 | bSizer2.Add( sbSizer1, 1, 0, 5 ) 37 | 38 | sbSizer2 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, _(u"EMail Account") ), wx.VERTICAL ) 39 | 40 | wSizer1 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 41 | 42 | self.m_staticText2 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, _(u"Email Address: "), wx.DefaultPosition, wx.Size( 80,-1 ), wx.ALIGN_LEFT ) 43 | self.m_staticText2.Wrap( -1 ) 44 | 45 | wSizer1.Add( self.m_staticText2, 0, wx.ALL, 8 ) 46 | 47 | self.email_account = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 360,-1 ), 0 ) 48 | wSizer1.Add( self.email_account, 0, wx.ALL, 5 ) 49 | 50 | self.m_staticText3 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, _(u"Password: "), wx.DefaultPosition, wx.Size( 80,-1 ), wx.ALIGN_LEFT ) 51 | self.m_staticText3.Wrap( -1 ) 52 | 53 | wSizer1.Add( self.m_staticText3, 0, wx.ALL, 8 ) 54 | 55 | self.password_input = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 260,-1 ), wx.TE_PASSWORD ) 56 | wSizer1.Add( self.password_input, 0, wx.ALL, 5 ) 57 | 58 | self.reme_ps_check = wx.CheckBox( sbSizer2.GetStaticBox(), wx.ID_ANY, _(u"Remember"), wx.DefaultPosition, wx.DefaultSize, 0 ) 59 | wSizer1.Add( self.reme_ps_check, 0, wx.ALL, 8 ) 60 | 61 | self.m_staticText4 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, _(u"SMTP Address: "), wx.DefaultPosition, wx.Size( 80,-1 ), wx.ALIGN_LEFT ) 62 | self.m_staticText4.Wrap( -1 ) 63 | 64 | wSizer1.Add( self.m_staticText4, 0, wx.ALL, 8 ) 65 | 66 | self.smtp_input = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 170,-1 ), 0 ) 67 | wSizer1.Add( self.smtp_input, 0, wx.ALL, 5 ) 68 | 69 | self.m_staticText5 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, _(u"Port: "), wx.DefaultPosition, wx.Size( 50,-1 ), wx.ALIGN_LEFT ) 70 | self.m_staticText5.Wrap( -1 ) 71 | 72 | wSizer1.Add( self.m_staticText5, 0, wx.ALL, 8 ) 73 | 74 | self.port_input = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 113,-1 ), 0 ) 75 | wSizer1.Add( self.port_input, 0, wx.ALL, 5 ) 76 | 77 | 78 | sbSizer2.Add( wSizer1, 1, wx.EXPAND, 5 ) 79 | 80 | 81 | bSizer2.Add( sbSizer2, 1, wx.EXPAND, 5 ) 82 | 83 | sbSizer3 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, _(u"Submission Type") ), wx.VERTICAL ) 84 | 85 | wSizer3 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 86 | 87 | self.false_neg_select = wx.RadioButton( sbSizer3.GetStaticBox(), wx.ID_ANY, _(u"False Negative"), wx.DefaultPosition, wx.Size( 100,-1 ), 0 ) 88 | self.false_neg_select.SetValue( True ) 89 | wSizer3.Add( self.false_neg_select, 0, wx.ALL, 5 ) 90 | 91 | self.false_positive_select = wx.RadioButton( sbSizer3.GetStaticBox(), wx.ID_ANY, _(u"False Positive"), wx.DefaultPosition, wx.Size( 100,-1 ), 0 ) 92 | wSizer3.Add( self.false_positive_select, 0, wx.ALL, 5 ) 93 | 94 | self.password_but = wx.Button( sbSizer3.GetStaticBox(), wx.ID_ANY, _(u"Zip Password"), wx.Point( -1,-1 ), wx.Size( 120,25 ), 0 ) 95 | wSizer3.Add( self.password_but, 0, wx.ALL, 0 ) 96 | 97 | self.email_content = wx.Button( sbSizer3.GetStaticBox(), wx.ID_ANY, _(u"Customize..."), wx.Point( -1,-1 ), wx.Size( 100,-1 ), 0 ) 98 | self.email_content.SetMinSize( wx.Size( 120,25 ) ) 99 | 100 | wSizer3.Add( self.email_content, 0, wx.ALL, 0 ) 101 | 102 | 103 | sbSizer3.Add( wSizer3, 1, wx.EXPAND, 0 ) 104 | 105 | 106 | bSizer2.Add( sbSizer3, 1, wx.EXPAND, 5 ) 107 | 108 | sbSizer4 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, _(u"Vendors") ), wx.VERTICAL ) 109 | 110 | wSizer4 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 111 | 112 | self.m_staticText6 = wx.StaticText( sbSizer4.GetStaticBox(), wx.ID_ANY, _(u"Selected Vendors: "), wx.DefaultPosition, wx.Size( 250,-1 ), 0 ) 113 | self.m_staticText6.Wrap( -1 ) 114 | 115 | wSizer4.Add( self.m_staticText6, 0, wx.ALL, 5 ) 116 | 117 | self.m_staticText7 = wx.StaticText( sbSizer4.GetStaticBox(), wx.ID_ANY, _(u"Vendor List: "), wx.DefaultPosition, wx.Size( 150,-1 ), 0 ) 118 | self.m_staticText7.Wrap( -1 ) 119 | 120 | wSizer4.Add( self.m_staticText7, 0, wx.ALL, 5 ) 121 | 122 | selected_vendorsChoices = [] 123 | self.selected_vendors = wx.ListBox( sbSizer4.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.Size( 190,100 ), selected_vendorsChoices, 0|wx.VSCROLL ) 124 | wSizer4.Add( self.selected_vendors, 0, wx.ALL, 5 ) 125 | 126 | bSizer5 = wx.BoxSizer( wx.VERTICAL ) 127 | 128 | self.add_but = wx.Button( sbSizer4.GetStaticBox(), wx.ID_ANY, _(u"<<"), wx.DefaultPosition, wx.Size( 50,-1 ), 0 ) 129 | bSizer5.Add( self.add_but, 0, wx.ALL, 5 ) 130 | 131 | self.remove_but = wx.Button( sbSizer4.GetStaticBox(), wx.ID_ANY, _(u">>"), wx.DefaultPosition, wx.Size( 50,-1 ), 0 ) 132 | bSizer5.Add( self.remove_but, 0, wx.ALL, 5 ) 133 | 134 | self.vendor_edit_but = wx.Button( sbSizer4.GetStaticBox(), wx.ID_ANY, _(u"..."), wx.DefaultPosition, wx.Size( 50,-1 ), 0 ) 135 | bSizer5.Add( self.vendor_edit_but, 0, wx.ALL, 5 ) 136 | 137 | 138 | wSizer4.Add( bSizer5, 1, wx.EXPAND, 5 ) 139 | 140 | vendor_listChoices = [ _(u"Avira;virus@avira.com"), _(u"Avira;novirus@avira.com"), _(u"Kaspersky;newvirus@kaspersky.com"), _(u"Kaspersky;apac-virussample@kaspersky.com"), _(u"ESET;samples@eset.sk"), _(u"ESET;samples@eset.com"), _(u"Huorong;seclab@huorong.cn"), _(u"BitDefender;oemsamples@bitdefender.com"), _(u"BitDefender;virus_submission@bitdefender.com") ] 141 | self.vendor_list = wx.ListBox( sbSizer4.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.Size( 200,100 ), vendor_listChoices, 0|wx.VSCROLL ) 142 | wSizer4.Add( self.vendor_list, 0, wx.ALL, 5 ) 143 | 144 | 145 | sbSizer4.Add( wSizer4, 1, wx.EXPAND, 5 ) 146 | 147 | 148 | bSizer2.Add( sbSizer4, 1, wx.EXPAND, 5 ) 149 | 150 | wSizer5 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 151 | 152 | sbSizer5 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, _(u"Other Settings: ") ), wx.VERTICAL ) 153 | 154 | self.m_staticText8 = wx.StaticText( sbSizer5.GetStaticBox(), wx.ID_ANY, _(u"Program Language: "), wx.DefaultPosition, wx.Size( 200,-1 ), 0 ) 155 | self.m_staticText8.Wrap( -1 ) 156 | 157 | sbSizer5.Add( self.m_staticText8, 0, wx.ALL, 5 ) 158 | 159 | language_choiceChoices = [ _(u"English"), _(u"Chinese Simplified") ] 160 | self.language_choice = wx.Choice( sbSizer5.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.Size( 200,-1 ), language_choiceChoices, 0 ) 161 | self.language_choice.SetSelection( 0 ) 162 | sbSizer5.Add( self.language_choice, 0, wx.ALL, 5 ) 163 | 164 | 165 | wSizer5.Add( sbSizer5, 1, wx.EXPAND, 5 ) 166 | 167 | bSizer7 = wx.BoxSizer( wx.VERTICAL ) 168 | 169 | self.pack_but = wx.Button( self, wx.ID_ANY, _(u"Pack All to Desktop"), wx.DefaultPosition, wx.Size( 250,30 ), 0 ) 170 | bSizer7.Add( self.pack_but, 0, wx.ALL, 3 ) 171 | 172 | self.submit_but = wx.Button( self, wx.ID_ANY, _(u"Pack and Submit All"), wx.DefaultPosition, wx.Size( 250,50 ), 0 ) 173 | bSizer7.Add( self.submit_but, 0, wx.ALL, 3 ) 174 | 175 | 176 | wSizer5.Add( bSizer7, 1, wx.EXPAND, 5 ) 177 | 178 | 179 | bSizer2.Add( wSizer5, 1, wx.EXPAND, 5 ) 180 | 181 | 182 | self.SetSizer( bSizer2 ) 183 | self.Layout() 184 | bSizer2.Fit( self ) 185 | 186 | self.Centre( wx.BOTH ) 187 | 188 | # Connect Events 189 | self.Bind( wx.EVT_CLOSE, self.close_but_click ) 190 | self.reme_ps_check.Bind( wx.EVT_CHECKBOX, self.reme_ps_check_click ) 191 | self.password_but.Bind( wx.EVT_BUTTON, self.password_but_click ) 192 | self.email_content.Bind( wx.EVT_BUTTON, self.content_but_click ) 193 | self.add_but.Bind( wx.EVT_BUTTON, self.add_but_click ) 194 | self.remove_but.Bind( wx.EVT_BUTTON, self.remove_but_click ) 195 | self.vendor_edit_but.Bind( wx.EVT_BUTTON, self.vendor_edit_but_click ) 196 | self.language_choice.Bind( wx.EVT_CHOICE, self.lang_select ) 197 | self.pack_but.Bind( wx.EVT_BUTTON, self.pack_but_click ) 198 | self.submit_but.Bind( wx.EVT_BUTTON, self.submit_but_click ) 199 | 200 | def __del__( self ): 201 | pass 202 | 203 | 204 | # Virtual event handlers, overide them in your derived class 205 | def close_but_click( self, event ): 206 | event.Skip() 207 | 208 | def reme_ps_check_click( self, event ): 209 | event.Skip() 210 | 211 | def password_but_click( self, event ): 212 | event.Skip() 213 | 214 | def content_but_click( self, event ): 215 | event.Skip() 216 | 217 | def add_but_click( self, event ): 218 | event.Skip() 219 | 220 | def remove_but_click( self, event ): 221 | event.Skip() 222 | 223 | def vendor_edit_but_click( self, event ): 224 | event.Skip() 225 | 226 | def lang_select( self, event ): 227 | event.Skip() 228 | 229 | def pack_but_click( self, event ): 230 | event.Skip() 231 | 232 | def submit_but_click( self, event ): 233 | event.Skip() 234 | 235 | 236 | ########################################################################### 237 | ## Class mail_content_frame 238 | ########################################################################### 239 | 240 | class mail_content_frame ( wx.Frame ): 241 | 242 | def __init__( self, parent ): 243 | wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = _(u"Custom Mail Content"), pos = wx.DefaultPosition, size = wx.Size( 500,270 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) 244 | 245 | self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 246 | self.SetBackgroundColour( wx.Colour( 255, 255, 255 ) ) 247 | 248 | bSizer4 = wx.BoxSizer( wx.VERTICAL ) 249 | 250 | wSizer6 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 251 | 252 | self.m_staticText10 = wx.StaticText( self, wx.ID_ANY, _(u"False Negative Text"), wx.DefaultPosition, wx.Size( 250,20 ), 0 ) 253 | self.m_staticText10.Wrap( -1 ) 254 | 255 | wSizer6.Add( self.m_staticText10, 0, wx.ALL, 5 ) 256 | 257 | self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, _(u"False Positive Text"), wx.DefaultPosition, wx.Size( -1,20 ), 0 ) 258 | self.m_staticText11.Wrap( -1 ) 259 | 260 | wSizer6.Add( self.m_staticText11, 0, wx.ALL, 5 ) 261 | 262 | 263 | bSizer4.Add( wSizer6, 1, wx.EXPAND, 5 ) 264 | 265 | wSizer7 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 266 | 267 | self.false_negative_content = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 230,150 ), wx.TE_MULTILINE|wx.VSCROLL ) 268 | wSizer7.Add( self.false_negative_content, 0, wx.ALL, 5 ) 269 | 270 | self.false_positive_content = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 230,150 ), wx.TE_MULTILINE|wx.VSCROLL ) 271 | wSizer7.Add( self.false_positive_content, 0, wx.ALL, 5 ) 272 | 273 | 274 | bSizer4.Add( wSizer7, 1, wx.EXPAND, 5 ) 275 | 276 | wSizer8 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) 277 | 278 | self.ok_but = wx.Button( self, wx.ID_ANY, _(u"OK"), wx.DefaultPosition, wx.DefaultSize, 0 ) 279 | wSizer8.Add( self.ok_but, 0, wx.ALL, 5 ) 280 | 281 | self.cancel_but = wx.Button( self, wx.ID_ANY, _(u"Cancel"), wx.DefaultPosition, wx.DefaultSize, 0 ) 282 | wSizer8.Add( self.cancel_but, 0, wx.ALL, 5 ) 283 | 284 | 285 | bSizer4.Add( wSizer8, 1, wx.ALIGN_RIGHT, 5 ) 286 | 287 | 288 | self.SetSizer( bSizer4 ) 289 | self.Layout() 290 | 291 | self.Centre( wx.BOTH ) 292 | 293 | # Connect Events 294 | self.Bind( wx.EVT_CLOSE, self.close_but_click ) 295 | self.ok_but.Bind( wx.EVT_BUTTON, self.ok_but_click ) 296 | self.cancel_but.Bind( wx.EVT_BUTTON, self.cancel_but_click ) 297 | 298 | def __del__( self ): 299 | pass 300 | 301 | 302 | # Virtual event handlers, overide them in your derived class 303 | def close_but_click( self, event ): 304 | event.Skip() 305 | 306 | def ok_but_click( self, event ): 307 | event.Skip() 308 | 309 | def cancel_but_click( self, event ): 310 | event.Skip() 311 | 312 | 313 | -------------------------------------------------------------------------------- /Main.py: -------------------------------------------------------------------------------- 1 | import GUI 2 | import wx 3 | import configparser 4 | import os 5 | import sys 6 | import Sender 7 | from distutils.util import strtobool 8 | from base64 import urlsafe_b64encode, urlsafe_b64decode 9 | 10 | # For i18n support 11 | _ = wx.GetTranslation 12 | 13 | 14 | class AppMainFrame(GUI.main_frame): 15 | """ 16 | Class for handling main_frame GUI event 17 | """ 18 | config: configparser.ConfigParser = None # config obj 19 | lists = None # vendor list 20 | zip_password: str = None # password for compressing 21 | false_negative_content: str = None # mail content for fn 22 | false_positive_content: str = None # main content for fp 23 | progress_bar: wx.ProgressDialog = None # sending progress dialog 24 | 25 | def __init__(self, parent, config, lists): 26 | """ 27 | Initialize the main window 28 | :param parent: wxframe 29 | :param config: config file 30 | :param lists: vendor list 31 | """ 32 | GUI.main_frame.__init__(self, parent) 33 | self.SetIcon(wx.Icon(app_path('mail.ico'), wx.BITMAP_TYPE_ICO)) 34 | self.config = config 35 | self.lists = lists 36 | self._ini_setting() 37 | self._ini_lists() 38 | self._ini_file_drop() 39 | 40 | def password_but_click(self, event): 41 | """Config Zip password""" 42 | ps_input = wx.TextEntryDialog(self, _('Enter password for compressing'), 43 | caption=_('Set Zip Password'), 44 | value=self.zip_password, 45 | style=wx.OK | wx.CANCEL) 46 | while True: 47 | if ps_input.ShowModal() == wx.ID_OK: 48 | ps = ps_input.GetValue() 49 | if ps == '': 50 | wx.MessageBox(message=_('Password Cannot be Empty'), 51 | caption=_('ERROR'), 52 | style=wx.OK | wx.ICON_ERROR) 53 | continue 54 | self.zip_password = ps_input.GetValue() 55 | break 56 | break 57 | ps_input.Destroy() 58 | 59 | def reme_ps_check_click(self, event): 60 | """Save email password to local file""" 61 | if self.reme_ps_check.GetValue(): 62 | result = wx.MessageBox(message=_('Password will be stored in the local config file. Continue?'), 63 | caption=_('WARNING'), 64 | style=wx.OK | wx.CANCEL | wx.ICON_WARNING) 65 | if result != wx.OK: 66 | self.reme_ps_check.SetValue(False) 67 | 68 | def add_but_click(self, event): 69 | """Add item to selected list from vendor list""" 70 | cur_index = self.vendor_list.GetSelection() 71 | if cur_index is wx.NOT_FOUND: 72 | return 73 | self.selected_vendors.Append(self.vendor_list.GetString(cur_index)) 74 | self.vendor_list.Delete(cur_index) 75 | 76 | def remove_but_click(self, event): 77 | """Remove item from selected list and send back to vendor list""" 78 | cur_index = self.selected_vendors.GetSelection() 79 | if cur_index is wx.NOT_FOUND: 80 | return 81 | self.vendor_list.Append(self.selected_vendors.GetString(cur_index)) 82 | self.selected_vendors.Delete(cur_index) 83 | 84 | def vendor_edit_but_click(self, event): 85 | """Edit vendor list""" 86 | vendor_input = wx.TextEntryDialog(self, _('Use ";" to separate name and email.'), 87 | caption=_('Edit Vendor List'), 88 | value=str(self.selected_vendors.GetStrings() + self.vendor_list.GetStrings()), 89 | style=wx.OK | wx.CANCEL | wx.TE_MULTILINE) 90 | while True: 91 | if vendor_input.ShowModal() == wx.ID_OK: 92 | try: 93 | self.vendor_list.Set(eval(vendor_input.GetValue())) # transfer str to dict 94 | self.selected_vendors.Clear() # reset vendor list 95 | break 96 | except: 97 | wx.MessageBox(message=_('Incorrect Data Format'), 98 | caption=_('ERROR'), 99 | style=wx.OK | wx.ICON_ERROR) 100 | continue 101 | else: 102 | break 103 | vendor_input.Destroy() 104 | 105 | def content_but_click(self, event): 106 | """Build mail content frame""" 107 | AppEmailContentFrame(parent=self).Show(True) 108 | return 109 | 110 | def submit_but_click(self, event): 111 | """Submit sample""" 112 | self.progress_bar = wx.ProgressDialog(title=_('Sending Samples...'), 113 | message='N/A', 114 | maximum=100, 115 | parent=self, 116 | style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) 117 | Sender.SendingThread(self) # initialize sending thread 118 | 119 | def pack_but_click(self, event): 120 | """Pack samples to desktop path""" 121 | pack = Sender.SampleFiles(self.file_input.GetValue(), self.zip_password) # compressing samples 122 | try: 123 | pack.compress_to_desktop() 124 | except Exception as e: 125 | wx.MessageBox(message=_('Cannot Access Sample File(s). Check paths.\n') + 126 | 'Error: {error}\nInfo: {info}'.format(error=e.__class__.__name__, info=str(e)), 127 | caption=_('ERROR'), 128 | style=wx.OK | wx.ICON_ERROR) 129 | pack.delete_zip() 130 | return 131 | 132 | wx.MessageBox(message=_('Pack to Desktop succeed!'), 133 | caption=_('INFO'), 134 | style=wx.OK | wx.ICON_INFORMATION) 135 | 136 | def close_but_click(self, event): 137 | """Operation before main_frame close""" 138 | self._save_setting() 139 | self._save_list() 140 | self.Destroy() 141 | 142 | def lang_select(self, event): 143 | """Select language and restart App""" 144 | self._save_setting() 145 | self._save_list() 146 | os.startfile(sys.argv[0]) # restart 147 | self.Destroy() 148 | 149 | def _ini_file_drop(self): 150 | """Set file drop target""" 151 | drop_target = FileDropTarget(self) 152 | self.file_input.SetDropTarget(drop_target) 153 | 154 | def _ini_lists(self): 155 | """Initialize vendor list""" 156 | if self.lists is not None: 157 | self.vendor_list.Set(self.lists['vendor_list']) 158 | self.selected_vendors.Set(self.lists['selected_list']) 159 | 160 | def _ini_setting(self): 161 | """parse all settings to GUI """ 162 | cf = self.config 163 | sec_str = 'main_frame_string' 164 | sec_bool = 'main_frame_boolean' 165 | sec_int = 'main_frame_int' 166 | sec_txt = 'main_frame_txt' 167 | # iterate to apply all settings 168 | if cf.has_section(sec_str): 169 | for key in cf[sec_str]: 170 | getattr(self, key).SetValue(cf[sec_str][key]) 171 | for key in cf[sec_bool]: 172 | getattr(self, key).SetValue(bool(strtobool(cf[sec_bool][key]))) 173 | for key in cf[sec_int]: 174 | getattr(self, key).SetSelection(int(cf[sec_int][key])) 175 | for key in cf[sec_txt]: 176 | setattr(self, key, cf[sec_txt][key]) 177 | data = self.password_input.GetValue() 178 | # decode email password 179 | self.password_input.SetValue(urlsafe_b64decode(data[2:len(data) - 1]).decode('utf-8')) 180 | else: 181 | # Ini setting if no previous setting found 182 | cf.add_section(sec_str) 183 | cf.add_section(sec_bool) 184 | cf.add_section(sec_int) 185 | cf.add_section(sec_txt) 186 | self.zip_password = 'infected' 187 | self.false_negative_content = _("Hello,\n\nThe attached files may contain threats. Require for the further " 188 | "analysis.\n\nPassword: {password} \n\nThanks! ") 189 | self.false_positive_content = _("Hello,\n\nThe attached files may be clean. Require for the further " 190 | "analysis.\n\nPassword: {password} \n\nThanks! ") 191 | 192 | def _save_list(self): 193 | """Save vendor list""" 194 | self.lists = { 195 | 'vendor_list': self.vendor_list.GetStrings(), 196 | 'selected_list': self.selected_vendors.GetStrings() 197 | } 198 | 199 | def _save_setting(self): 200 | """Save GUI settings to config obj""" 201 | cf = self.config 202 | sec = 'main_frame_string' 203 | cf.set(sec, 'email_account', self.email_account.GetValue()) 204 | # convert password to base64 for basic obfuscation 205 | cf.set(sec, 'password_input', str(urlsafe_b64encode(bytes(self.password_input.GetValue(), 'utf-8')))) 206 | cf.set(sec, 'smtp_input', self.smtp_input.GetValue()) 207 | cf.set(sec, 'port_input', self.port_input.GetValue()) 208 | if self.reme_ps_check.GetValue() is False: 209 | cf.set(sec, 'password_input', '') 210 | 211 | sec = 'main_frame_boolean' 212 | cf.set(sec, 'reme_ps_check', str(self.reme_ps_check.GetValue())) 213 | cf.set(sec, 'false_neg_select', str(self.false_neg_select.GetValue())) 214 | cf.set(sec, 'false_positive_select', str(self.false_positive_select.GetValue())) 215 | 216 | sec = 'main_frame_int' 217 | cf.set(sec, 'language_choice', str(self.language_choice.GetCurrentSelection())) 218 | 219 | sec = 'main_frame_txt' 220 | cf.set(sec, 'zip_password', str(self.zip_password)) 221 | cf.set(sec, 'false_negative_content', str(self.false_negative_content)) 222 | cf.set(sec, 'false_positive_content', str(self.false_positive_content)) 223 | 224 | 225 | class AppEmailContentFrame(GUI.mail_content_frame): 226 | """ 227 | Class for handing mail_content_frame event 228 | """ 229 | parent_f: AppMainFrame = None # parent frame 230 | 231 | def __init__(self, parent): 232 | """ 233 | Initialize frame window 234 | :param parent: wxframe 235 | """ 236 | GUI.mail_content_frame.__init__(self, parent) 237 | self.parent_f = parent 238 | self.parent_f.Disable() 239 | self.false_negative_content.SetValue(self.parent_f.false_negative_content) 240 | self.false_positive_content.SetValue(self.parent_f.false_positive_content) 241 | 242 | def return_to_parent(self): 243 | """Operation before closing this frame""" 244 | self.parent_f.Enable() 245 | self.parent_f.SetFocus() 246 | self.Destroy() 247 | 248 | def ok_but_click(self, event): 249 | self.parent_f.false_negative_content = self.false_negative_content.GetValue() 250 | self.parent_f.false_positive_content = self.false_positive_content.GetValue() 251 | self.return_to_parent() 252 | 253 | def cancel_but_click(self, event): 254 | self.return_to_parent() 255 | 256 | def close_but_click(self, event): 257 | self.return_to_parent() 258 | 259 | 260 | class FileDropTarget(wx.FileDropTarget): 261 | """ 262 | Config drop target obj 263 | """ 264 | frame_p: AppMainFrame = None # frame parameter 265 | 266 | def __init__(self, frame_p): 267 | wx.FileDropTarget.__init__(self) 268 | self.frame_p = frame_p 269 | 270 | def OnDropFiles(self, x, y, data: list): 271 | previous_txt = self.frame_p.file_input.GetValue() 272 | # Check if input textCtrl is empty or start with '#' (Default) 273 | if previous_txt == '' or previous_txt[0] == "#": 274 | previous_txt = '' 275 | self.frame_p.file_input.SetValue(previous_txt) 276 | else: 277 | previous_txt = previous_txt + '\n' 278 | self.frame_p.file_input.SetValue(previous_txt + '\n'.join(data)) 279 | return True 280 | 281 | 282 | def app_path(path): 283 | """ 284 | Static method to get the abs path special for pyinstaller packing 285 | :param path: relative path 286 | :return: abs path 287 | """ 288 | if getattr(sys, 'frozen', False): 289 | app_dir = sys._MEIPASS 290 | else: 291 | app_dir = os.path.dirname(os.path.abspath(__file__)) 292 | return os.path.join(app_dir, path) 293 | 294 | 295 | class BaseApp(wx.App): 296 | """ 297 | Class for config wxApp; Loading config and language file 298 | """ 299 | config_path = os.getenv('APPDATA') + "\\VirusSampleSubmitter" # config file path 300 | config = configparser.ConfigParser() # config obj 301 | lists = None # vendor list 302 | 303 | def OnInit(self): 304 | """Load config and list file""" 305 | if os.path.exists(self.config_path) is False: 306 | os.mkdir(self.config_path) 307 | self.config.read(self.config_path + "\\config.ini") 308 | self._ini_language() 309 | if os.path.exists(self.config_path + "\\list.db") is False: 310 | return True 311 | with open(self.config_path + "\\list.db") as f: 312 | self.lists = eval(f.read()) 313 | f.close() 314 | return True 315 | 316 | def OnExit(self): 317 | """Save config and list file""" 318 | self.lists = frame.lists 319 | with open(self.config_path + "\\config.ini", 'w') as config_w: 320 | self.config.write(config_w) 321 | config_w.close() 322 | with open(self.config_path + "\\list.db", "w") as f: 323 | f.write(str(self.lists)) 324 | f.close() 325 | return True 326 | 327 | def _ini_language(self): 328 | """Initialize program language""" 329 | self.locale = None 330 | if not self.config.has_section('main_frame_int'): 331 | return 332 | lang = int(self.config['main_frame_int']['language_choice']) 333 | if lang == 1: # Change to zh-cn 334 | self.locale = wx.Locale(wx.LANGUAGE_CHINESE_SIMPLIFIED) 335 | if self.locale.IsOk(): 336 | self.locale.AddCatalogLookupPathPrefix(app_path('locale')) 337 | self.locale.AddCatalog('lang_zh_CN') 338 | else: 339 | self.locale = None 340 | 341 | 342 | if __name__ == '__main__': 343 | app = BaseApp(redirect=False) 344 | frame = AppMainFrame(None, app.config, app.lists) 345 | frame.Show(True) 346 | app.MainLoop() 347 | -------------------------------------------------------------------------------- /GUI.fbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ; 6 | C++ 7 | 1 8 | source_name 9 | 0 10 | 0 11 | res 12 | UTF-8 13 | connect 14 | 15 | 1000 16 | none 17 | 18 | 1 19 | MyProject2 20 | 21 | . 22 | 23 | 1 24 | 1 25 | 1 26 | 1 27 | UI 28 | 0 29 | 0 30 | 31 | 0 32 | wxAUI_MGR_DEFAULT 33 | wxSYS_COLOUR_WINDOW 34 | wxBOTH 35 | 36 | 1 37 | 1 38 | impl_virtual 39 | 40 | 41 | 42 | 0 43 | wxID_ANY 44 | 500,600 45 | 46 | main_frame 47 | 48 | -1,-1 49 | wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX 50 | ; ; forward_declare 51 | Sample Mail Submitter 52 | 53 | 54 | 55 | wxTAB_TRAVERSAL 56 | 1 57 | close_but_click 58 | 59 | 60 | bSizer2 61 | wxVERTICAL 62 | none 63 | 64 | 5 65 | 66 | 1 67 | 68 | wxID_ANY 69 | Choose File(s) 70 | -1,-1 71 | sbSizer1 72 | wxVERTICAL 73 | 1 74 | none 75 | 76 | 5 77 | wxALL 78 | 0 79 | 80 | 1 81 | 1 82 | 1 83 | 1 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 1 92 | 0 93 | 1 94 | 95 | 1 96 | 0 97 | Dock 98 | 0 99 | Left 100 | 1 101 | 102 | 1 103 | 104 | 0 105 | 0 106 | wxID_ANY 107 | 108 | 0 109 | 110 | 111 | 112 | 0 113 | 114 | 1 115 | file_input 116 | 1 117 | 118 | 119 | protected 120 | 1 121 | 122 | Resizable 123 | 1 124 | 500,100 125 | wxTE_MULTILINE 126 | ; ; forward_declare 127 | 0 128 | 129 | 130 | wxFILTER_NONE 131 | wxDefaultValidator 132 | 133 | #Drag all file(s) here. One line per file. 134 | 135 | 136 | wxHSCROLL|wxVSCROLL 137 | 138 | 139 | 140 | 141 | 142 | 5 143 | wxEXPAND 144 | 1 145 | 146 | wxID_ANY 147 | EMail Account 148 | 149 | sbSizer2 150 | wxVERTICAL 151 | 1 152 | none 153 | 154 | 5 155 | wxEXPAND 156 | 1 157 | 158 | wxWRAPSIZER_DEFAULT_FLAGS 159 | 160 | wSizer1 161 | wxHORIZONTAL 162 | none 163 | 164 | 8 165 | wxALL 166 | 0 167 | 168 | 1 169 | 1 170 | 1 171 | 1 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 1 180 | 0 181 | 1 182 | 183 | 1 184 | 0 185 | Dock 186 | 0 187 | Left 188 | 1 189 | 190 | 1 191 | 192 | 0 193 | 0 194 | wxID_ANY 195 | Email Address: 196 | 0 197 | 198 | 0 199 | 200 | 201 | 0 202 | -1,-1 203 | 1 204 | m_staticText2 205 | 1 206 | 207 | 208 | protected 209 | 1 210 | 211 | Resizable 212 | 1 213 | 80,-1 214 | wxALIGN_LEFT 215 | ; ; forward_declare 216 | 0 217 | 218 | 219 | 220 | 221 | -1 222 | 223 | 224 | 225 | 5 226 | wxALL 227 | 0 228 | 229 | 1 230 | 1 231 | 1 232 | 1 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 1 241 | 0 242 | 1 243 | 244 | 1 245 | 0 246 | Dock 247 | 0 248 | Left 249 | 1 250 | 251 | 1 252 | 253 | 0 254 | 0 255 | wxID_ANY 256 | 257 | 0 258 | 259 | 260 | 261 | 0 262 | 263 | 1 264 | email_account 265 | 1 266 | 267 | 268 | protected 269 | 1 270 | 271 | Resizable 272 | 1 273 | 360,-1 274 | 275 | ; ; forward_declare 276 | 0 277 | 278 | 279 | wxFILTER_NONE 280 | wxDefaultValidator 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 8 290 | wxALL 291 | 0 292 | 293 | 1 294 | 1 295 | 1 296 | 1 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 1 305 | 0 306 | 1 307 | 308 | 1 309 | 0 310 | Dock 311 | 0 312 | Left 313 | 1 314 | 315 | 1 316 | 317 | 0 318 | 0 319 | wxID_ANY 320 | Password: 321 | 0 322 | 323 | 0 324 | 325 | 326 | 0 327 | -1,-1 328 | 1 329 | m_staticText3 330 | 1 331 | 332 | 333 | protected 334 | 1 335 | 336 | Resizable 337 | 1 338 | 80,-1 339 | wxALIGN_LEFT 340 | ; ; forward_declare 341 | 0 342 | 343 | 344 | 345 | 346 | -1 347 | 348 | 349 | 350 | 5 351 | wxALL 352 | 0 353 | 354 | 1 355 | 1 356 | 1 357 | 1 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 1 366 | 0 367 | 1 368 | 369 | 1 370 | 0 371 | Dock 372 | 0 373 | Left 374 | 1 375 | 376 | 1 377 | 378 | 0 379 | 0 380 | wxID_ANY 381 | 382 | 0 383 | 384 | 385 | 386 | 0 387 | 388 | 1 389 | password_input 390 | 1 391 | 392 | 393 | protected 394 | 1 395 | 396 | Resizable 397 | 1 398 | 260,-1 399 | wxTE_PASSWORD 400 | ; ; forward_declare 401 | 0 402 | 403 | 404 | wxFILTER_NONE 405 | wxDefaultValidator 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 8 415 | wxALL 416 | 0 417 | 418 | 1 419 | 1 420 | 1 421 | 1 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 1 430 | 0 431 | 0 432 | 1 433 | 434 | 1 435 | 0 436 | Dock 437 | 0 438 | Left 439 | 1 440 | 441 | 1 442 | 443 | 0 444 | 0 445 | wxID_ANY 446 | Remember 447 | 448 | 0 449 | 450 | 451 | 0 452 | 453 | 1 454 | reme_ps_check 455 | 1 456 | 457 | 458 | protected 459 | 1 460 | 461 | Resizable 462 | 1 463 | 464 | 465 | ; ; forward_declare 466 | 0 467 | 468 | 469 | wxFILTER_NONE 470 | wxDefaultValidator 471 | 472 | 473 | 474 | 475 | reme_ps_check_click 476 | 477 | 478 | 479 | 8 480 | wxALL 481 | 0 482 | 483 | 1 484 | 1 485 | 1 486 | 1 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 1 495 | 0 496 | 1 497 | 498 | 1 499 | 0 500 | Dock 501 | 0 502 | Left 503 | 1 504 | 505 | 1 506 | 507 | 0 508 | 0 509 | wxID_ANY 510 | SMTP Address: 511 | 0 512 | 513 | 0 514 | 515 | 516 | 0 517 | 518 | 1 519 | m_staticText4 520 | 1 521 | 522 | 523 | protected 524 | 1 525 | 526 | Resizable 527 | 1 528 | 80,-1 529 | wxALIGN_LEFT 530 | ; ; forward_declare 531 | 0 532 | 533 | 534 | 535 | 536 | -1 537 | 538 | 539 | 540 | 5 541 | wxALL 542 | 0 543 | 544 | 1 545 | 1 546 | 1 547 | 1 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 1 556 | 0 557 | 1 558 | 559 | 1 560 | 0 561 | Dock 562 | 0 563 | Left 564 | 1 565 | 566 | 1 567 | 568 | 0 569 | 0 570 | wxID_ANY 571 | 572 | 0 573 | 574 | 575 | 576 | 0 577 | 578 | 1 579 | smtp_input 580 | 1 581 | 582 | 583 | protected 584 | 1 585 | 586 | Resizable 587 | 1 588 | 170,-1 589 | 590 | ; ; forward_declare 591 | 0 592 | 593 | 594 | wxFILTER_NONE 595 | wxDefaultValidator 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 8 605 | wxALL 606 | 0 607 | 608 | 1 609 | 1 610 | 1 611 | 1 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 1 620 | 0 621 | 1 622 | 623 | 1 624 | 0 625 | Dock 626 | 0 627 | Left 628 | 1 629 | 630 | 1 631 | 632 | 0 633 | 0 634 | wxID_ANY 635 | Port: 636 | 0 637 | 638 | 0 639 | 640 | 641 | 0 642 | 643 | 1 644 | m_staticText5 645 | 1 646 | 647 | 648 | protected 649 | 1 650 | 651 | Resizable 652 | 1 653 | 50,-1 654 | wxALIGN_LEFT 655 | ; ; forward_declare 656 | 0 657 | 658 | 659 | 660 | 661 | -1 662 | 663 | 664 | 665 | 5 666 | wxALL 667 | 0 668 | 669 | 1 670 | 1 671 | 1 672 | 1 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 1 681 | 0 682 | 1 683 | 684 | 1 685 | 0 686 | Dock 687 | 0 688 | Left 689 | 1 690 | 691 | 1 692 | 693 | 0 694 | 0 695 | wxID_ANY 696 | 697 | 0 698 | 699 | 700 | 701 | 0 702 | 703 | 1 704 | port_input 705 | 1 706 | 707 | 708 | protected 709 | 1 710 | 711 | Resizable 712 | 1 713 | 113,-1 714 | 715 | ; ; forward_declare 716 | 0 717 | 718 | 719 | wxFILTER_NONE 720 | wxDefaultValidator 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 5 734 | wxEXPAND 735 | 1 736 | 737 | wxID_ANY 738 | Submission Type 739 | -1,-1 740 | sbSizer3 741 | wxVERTICAL 742 | 1 743 | none 744 | 745 | 0 746 | wxEXPAND 747 | 1 748 | 749 | wxWRAPSIZER_DEFAULT_FLAGS 750 | 751 | wSizer3 752 | wxHORIZONTAL 753 | none 754 | 755 | 5 756 | wxALL 757 | 0 758 | 759 | 1 760 | 1 761 | 1 762 | 1 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 1 771 | 0 772 | 1 773 | 774 | 1 775 | 0 776 | Dock 777 | 0 778 | Left 779 | 1 780 | 781 | 1 782 | 783 | 0 784 | 0 785 | wxID_ANY 786 | False Negative 787 | 788 | 0 789 | 790 | 791 | 0 792 | 793 | 1 794 | false_neg_select 795 | 1 796 | 797 | 798 | protected 799 | 1 800 | 801 | Resizable 802 | 1 803 | 100,-1 804 | 805 | ; ; forward_declare 806 | 0 807 | 808 | 809 | wxFILTER_NONE 810 | wxDefaultValidator 811 | 812 | 1 813 | 814 | 815 | 816 | 817 | 818 | 819 | 5 820 | wxALL 821 | 0 822 | 823 | 1 824 | 1 825 | 1 826 | 1 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 1 835 | 0 836 | 1 837 | 838 | 1 839 | 0 840 | Dock 841 | 0 842 | Left 843 | 1 844 | 845 | 1 846 | 847 | 0 848 | 0 849 | wxID_ANY 850 | False Positive 851 | 852 | 0 853 | 854 | 855 | 0 856 | 857 | 1 858 | false_positive_select 859 | 1 860 | 861 | 862 | protected 863 | 1 864 | 865 | Resizable 866 | 1 867 | 100,-1 868 | 869 | ; ; forward_declare 870 | 0 871 | 872 | 873 | wxFILTER_NONE 874 | wxDefaultValidator 875 | 876 | 0 877 | 878 | 879 | 880 | 881 | 882 | 883 | 0 884 | wxALL 885 | 0 886 | 887 | 1 888 | 1 889 | 1 890 | 1 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 1 900 | 0 901 | 1 902 | 903 | 1 904 | 905 | 0 906 | 0 907 | 908 | Dock 909 | 0 910 | Left 911 | 1 912 | 913 | 1 914 | 915 | 916 | 0 917 | 0 918 | wxID_ANY 919 | Zip Password 920 | 921 | 0 922 | 923 | 0 924 | 925 | 926 | 0 927 | 928 | 1 929 | password_but 930 | 1 931 | 932 | 933 | protected 934 | 1 935 | -1,-1 936 | 937 | 938 | Resizable 939 | 1 940 | 120,25 941 | 942 | ; ; forward_declare 943 | 0 944 | 945 | 946 | wxFILTER_NONE 947 | wxDefaultValidator 948 | 949 | 950 | 951 | 952 | password_but_click 953 | 954 | 955 | 956 | 0 957 | wxALL 958 | 0 959 | 960 | 1 961 | 1 962 | 1 963 | 1 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 1 973 | 0 974 | 1 975 | 976 | 1 977 | 978 | 0 979 | 0 980 | 981 | Dock 982 | 0 983 | Left 984 | 1 985 | 986 | 1 987 | 988 | 989 | 0 990 | 0 991 | wxID_ANY 992 | Customize... 993 | 994 | 0 995 | 996 | 0 997 | 998 | 999 | 0 1000 | 120,25 1001 | 1 1002 | email_content 1003 | 1 1004 | 1005 | 1006 | protected 1007 | 1 1008 | -1,-1 1009 | 1010 | 1011 | Resizable 1012 | 1 1013 | 100,-1 1014 | 1015 | ; ; forward_declare 1016 | 0 1017 | 1018 | 1019 | wxFILTER_NONE 1020 | wxDefaultValidator 1021 | 1022 | 1023 | 1024 | 1025 | content_but_click 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 5 1034 | wxEXPAND 1035 | 1 1036 | 1037 | wxID_ANY 1038 | Vendors 1039 | -1,-1 1040 | sbSizer4 1041 | wxVERTICAL 1042 | 1 1043 | none 1044 | 1045 | 5 1046 | wxEXPAND 1047 | 1 1048 | 1049 | wxWRAPSIZER_DEFAULT_FLAGS 1050 | 1051 | wSizer4 1052 | wxHORIZONTAL 1053 | none 1054 | 1055 | 5 1056 | wxALL 1057 | 0 1058 | 1059 | 1 1060 | 1 1061 | 1 1062 | 1 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1 1071 | 0 1072 | 1 1073 | 1074 | 1 1075 | 0 1076 | Dock 1077 | 0 1078 | Left 1079 | 1 1080 | 1081 | 1 1082 | 1083 | 0 1084 | 0 1085 | wxID_ANY 1086 | Selected Vendors: 1087 | 0 1088 | 1089 | 0 1090 | 1091 | 1092 | 0 1093 | 1094 | 1 1095 | m_staticText6 1096 | 1 1097 | 1098 | 1099 | protected 1100 | 1 1101 | 1102 | Resizable 1103 | 1 1104 | 250,-1 1105 | 1106 | ; ; forward_declare 1107 | 0 1108 | 1109 | 1110 | 1111 | 1112 | -1 1113 | 1114 | 1115 | 1116 | 5 1117 | wxALL 1118 | 0 1119 | 1120 | 1 1121 | 1 1122 | 1 1123 | 1 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1 1132 | 0 1133 | 1 1134 | 1135 | 1 1136 | 0 1137 | Dock 1138 | 0 1139 | Left 1140 | 1 1141 | 1142 | 1 1143 | 1144 | 0 1145 | 0 1146 | wxID_ANY 1147 | Vendor List: 1148 | 0 1149 | 1150 | 0 1151 | 1152 | 1153 | 0 1154 | 1155 | 1 1156 | m_staticText7 1157 | 1 1158 | 1159 | 1160 | protected 1161 | 1 1162 | 1163 | Resizable 1164 | 1 1165 | 150,-1 1166 | 1167 | ; ; forward_declare 1168 | 0 1169 | 1170 | 1171 | 1172 | 1173 | -1 1174 | 1175 | 1176 | 1177 | 5 1178 | wxALL 1179 | 0 1180 | 1181 | 1 1182 | 1 1183 | 1 1184 | 1 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1 1193 | 0 1194 | 1195 | 1 1196 | 1197 | 1 1198 | 0 1199 | Dock 1200 | 0 1201 | Left 1202 | 1 1203 | 1204 | 1 1205 | 1206 | 0 1207 | 0 1208 | wxID_ANY 1209 | 1210 | 0 1211 | 1212 | 1213 | 0 1214 | 1215 | 1 1216 | selected_vendors 1217 | 1 1218 | 1219 | 1220 | protected 1221 | 1 1222 | 1223 | Resizable 1224 | 1 1225 | 190,100 1226 | 1227 | ; ; forward_declare 1228 | 0 1229 | 1230 | 1231 | wxFILTER_NONE 1232 | wxDefaultValidator 1233 | 1234 | 1235 | 1236 | wxVSCROLL 1237 | 1238 | 1239 | 1240 | 5 1241 | wxEXPAND 1242 | 1 1243 | 1244 | 1245 | bSizer5 1246 | wxVERTICAL 1247 | none 1248 | 1249 | 5 1250 | wxALL 1251 | 0 1252 | 1253 | 1 1254 | 1 1255 | 1 1256 | 1 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1 1266 | 0 1267 | 1 1268 | 1269 | 1 1270 | 1271 | 0 1272 | 0 1273 | 1274 | Dock 1275 | 0 1276 | Left 1277 | 1 1278 | 1279 | 1 1280 | 1281 | 1282 | 0 1283 | 0 1284 | wxID_ANY 1285 | << 1286 | 1287 | 0 1288 | 1289 | 0 1290 | 1291 | 1292 | 0 1293 | 1294 | 1 1295 | add_but 1296 | 1 1297 | 1298 | 1299 | protected 1300 | 1 1301 | 1302 | 1303 | 1304 | Resizable 1305 | 1 1306 | 50,-1 1307 | 1308 | ; ; forward_declare 1309 | 0 1310 | 1311 | 1312 | wxFILTER_NONE 1313 | wxDefaultValidator 1314 | 1315 | 1316 | 1317 | 1318 | add_but_click 1319 | 1320 | 1321 | 1322 | 5 1323 | wxALL 1324 | 0 1325 | 1326 | 1 1327 | 1 1328 | 1 1329 | 1 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1 1339 | 0 1340 | 1 1341 | 1342 | 1 1343 | 1344 | 0 1345 | 0 1346 | 1347 | Dock 1348 | 0 1349 | Left 1350 | 1 1351 | 1352 | 1 1353 | 1354 | 1355 | 0 1356 | 0 1357 | wxID_ANY 1358 | >> 1359 | 1360 | 0 1361 | 1362 | 0 1363 | 1364 | 1365 | 0 1366 | 1367 | 1 1368 | remove_but 1369 | 1 1370 | 1371 | 1372 | protected 1373 | 1 1374 | 1375 | 1376 | 1377 | Resizable 1378 | 1 1379 | 50,-1 1380 | 1381 | ; ; forward_declare 1382 | 0 1383 | 1384 | 1385 | wxFILTER_NONE 1386 | wxDefaultValidator 1387 | 1388 | 1389 | 1390 | 1391 | remove_but_click 1392 | 1393 | 1394 | 1395 | 5 1396 | wxALL 1397 | 0 1398 | 1399 | 1 1400 | 1 1401 | 1 1402 | 1 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1 1412 | 0 1413 | 1 1414 | 1415 | 1 1416 | 1417 | 0 1418 | 0 1419 | 1420 | Dock 1421 | 0 1422 | Left 1423 | 1 1424 | 1425 | 1 1426 | 1427 | 1428 | 0 1429 | 0 1430 | wxID_ANY 1431 | ... 1432 | 1433 | 0 1434 | 1435 | 0 1436 | 1437 | 1438 | 0 1439 | 1440 | 1 1441 | vendor_edit_but 1442 | 1 1443 | 1444 | 1445 | protected 1446 | 1 1447 | 1448 | 1449 | 1450 | Resizable 1451 | 1 1452 | 50,-1 1453 | 1454 | ; ; forward_declare 1455 | 0 1456 | 1457 | 1458 | wxFILTER_NONE 1459 | wxDefaultValidator 1460 | 1461 | 1462 | 1463 | 1464 | vendor_edit_but_click 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 5 1471 | wxALL 1472 | 0 1473 | 1474 | 1 1475 | 1 1476 | 1 1477 | 1 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 | 1485 | 1 1486 | 0 1487 | "Avira;virus@avira.com" "Avira;novirus@avira.com" "Kaspersky;newvirus@kaspersky.com" "Kaspersky;apac-virussample@kaspersky.com" "ESET;samples@eset.sk" "ESET;samples@eset.com" "Huorong;seclab@huorong.cn" "BitDefender;oemsamples@bitdefender.com" "BitDefender;virus_submission@bitdefender.com" 1488 | 1 1489 | 1490 | 1 1491 | 0 1492 | Dock 1493 | 0 1494 | Left 1495 | 1 1496 | 1497 | 1 1498 | 1499 | 0 1500 | 0 1501 | wxID_ANY 1502 | 1503 | 0 1504 | 1505 | 1506 | 0 1507 | -1,-1 1508 | 1 1509 | vendor_list 1510 | 1 1511 | 1512 | 1513 | protected 1514 | 1 1515 | 1516 | Resizable 1517 | 1 1518 | 200,100 1519 | 1520 | ; ; forward_declare 1521 | 0 1522 | 1523 | 1524 | wxFILTER_NONE 1525 | wxDefaultValidator 1526 | 1527 | 1528 | 1529 | wxVSCROLL 1530 | 1531 | 1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 5 1538 | wxEXPAND 1539 | 1 1540 | 1541 | wxWRAPSIZER_DEFAULT_FLAGS 1542 | 1543 | wSizer5 1544 | wxHORIZONTAL 1545 | none 1546 | 1547 | 5 1548 | wxEXPAND 1549 | 1 1550 | 1551 | wxID_ANY 1552 | Other Settings: 1553 | 1554 | sbSizer5 1555 | wxVERTICAL 1556 | 1 1557 | none 1558 | 1559 | 5 1560 | wxALL 1561 | 0 1562 | 1563 | 1 1564 | 1 1565 | 1 1566 | 1 1567 | 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | 1 1575 | 0 1576 | 1 1577 | 1578 | 1 1579 | 0 1580 | Dock 1581 | 0 1582 | Left 1583 | 1 1584 | 1585 | 1 1586 | 1587 | 0 1588 | 0 1589 | wxID_ANY 1590 | Program Language: 1591 | 0 1592 | 1593 | 0 1594 | 1595 | 1596 | 0 1597 | 1598 | 1 1599 | m_staticText8 1600 | 1 1601 | 1602 | 1603 | protected 1604 | 1 1605 | 1606 | Resizable 1607 | 1 1608 | 200,-1 1609 | 1610 | ; ; forward_declare 1611 | 0 1612 | 1613 | 1614 | 1615 | 1616 | -1 1617 | 1618 | 1619 | 1620 | 5 1621 | wxALL 1622 | 0 1623 | 1624 | 1 1625 | 1 1626 | 1 1627 | 1 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1 1636 | 0 1637 | "English" "Chinese Simplified" 1638 | 1 1639 | 1640 | 1 1641 | 0 1642 | Dock 1643 | 0 1644 | Left 1645 | 1 1646 | 1647 | 1 1648 | 1649 | 0 1650 | 0 1651 | wxID_ANY 1652 | 1653 | 0 1654 | 1655 | 1656 | 0 1657 | 1658 | 1 1659 | language_choice 1660 | 1 1661 | 1662 | 1663 | protected 1664 | 1 1665 | 1666 | Resizable 1667 | 0 1668 | 1 1669 | 200,-1 1670 | 1671 | ; ; forward_declare 1672 | 0 1673 | 1674 | 1675 | wxFILTER_NONE 1676 | wxDefaultValidator 1677 | 1678 | 1679 | 1680 | 1681 | lang_select 1682 | 1683 | 1684 | 1685 | 1686 | 1687 | 5 1688 | wxEXPAND 1689 | 1 1690 | 1691 | 1692 | bSizer7 1693 | wxVERTICAL 1694 | none 1695 | 1696 | 3 1697 | wxALL 1698 | 0 1699 | 1700 | 1 1701 | 1 1702 | 1 1703 | 1 1704 | 1705 | 1706 | 1707 | 1708 | 1709 | 1710 | 1711 | 1712 | 1 1713 | 0 1714 | 1 1715 | 1716 | 1 1717 | 1718 | 0 1719 | 0 1720 | 1721 | Dock 1722 | 0 1723 | Left 1724 | 1 1725 | 1726 | 1 1727 | 1728 | 1729 | 0 1730 | 0 1731 | wxID_ANY 1732 | Pack All to Desktop 1733 | -1,-1 1734 | 0 1735 | 1736 | 0 1737 | 1738 | 1739 | 0 1740 | 1741 | 1 1742 | pack_but 1743 | 1 1744 | 1745 | 1746 | protected 1747 | 1 1748 | 1749 | 1750 | 1751 | Resizable 1752 | 1 1753 | 250,30 1754 | 1755 | ; ; forward_declare 1756 | 0 1757 | 1758 | 1759 | wxFILTER_NONE 1760 | wxDefaultValidator 1761 | 1762 | 1763 | 1764 | 1765 | pack_but_click 1766 | 1767 | 1768 | 1769 | 3 1770 | wxALL 1771 | 0 1772 | 1773 | 1 1774 | 1 1775 | 1 1776 | 1 1777 | 1778 | 1779 | 1780 | 1781 | 1782 | 1783 | 1784 | 1785 | 1 1786 | 0 1787 | 1 1788 | 1789 | 1 1790 | 1791 | 0 1792 | 0 1793 | 1794 | Dock 1795 | 0 1796 | Left 1797 | 1 1798 | 1799 | 1 1800 | 1801 | 1802 | 0 1803 | 0 1804 | wxID_ANY 1805 | Pack and Submit All 1806 | 1807 | 0 1808 | 1809 | 0 1810 | 1811 | 1812 | 0 1813 | 1814 | 1 1815 | submit_but 1816 | 1 1817 | 1818 | 1819 | protected 1820 | 1 1821 | 1822 | 1823 | 1824 | Resizable 1825 | 1 1826 | 250,50 1827 | 1828 | ; ; forward_declare 1829 | 0 1830 | 1831 | 1832 | wxFILTER_NONE 1833 | wxDefaultValidator 1834 | 1835 | 1836 | 1837 | 1838 | submit_but_click 1839 | 1840 | 1841 | 1842 | 1843 | 1844 | 1845 | 1846 | 1847 | 1848 | 0 1849 | wxAUI_MGR_DEFAULT 1850 | 255,255,255 1851 | wxBOTH 1852 | 1853 | 1 1854 | 1 1855 | impl_virtual 1856 | 1857 | 1858 | 1859 | 0 1860 | wxID_ANY 1861 | 1862 | 1863 | mail_content_frame 1864 | 1865 | 500,270 1866 | wxDEFAULT_FRAME_STYLE 1867 | ; ; forward_declare 1868 | Custom Mail Content 1869 | 1870 | 1871 | 1872 | wxTAB_TRAVERSAL 1873 | 1 1874 | close_but_click 1875 | 1876 | 1877 | bSizer4 1878 | wxVERTICAL 1879 | none 1880 | 1881 | 5 1882 | wxEXPAND 1883 | 1 1884 | 1885 | wxWRAPSIZER_DEFAULT_FLAGS 1886 | 1887 | wSizer6 1888 | wxHORIZONTAL 1889 | none 1890 | 1891 | 5 1892 | wxALL 1893 | 0 1894 | 1895 | 1 1896 | 1 1897 | 1 1898 | 1 1899 | 1900 | 1901 | 1902 | 1903 | 1904 | 1905 | 1906 | 1 1907 | 0 1908 | 1 1909 | 1910 | 1 1911 | 0 1912 | Dock 1913 | 0 1914 | Left 1915 | 1 1916 | 1917 | 1 1918 | 1919 | 0 1920 | 0 1921 | wxID_ANY 1922 | False Negative Text 1923 | 0 1924 | 1925 | 0 1926 | 1927 | 1928 | 0 1929 | 1930 | 1 1931 | m_staticText10 1932 | 1 1933 | 1934 | 1935 | protected 1936 | 1 1937 | 1938 | Resizable 1939 | 1 1940 | 250,20 1941 | 1942 | ; ; forward_declare 1943 | 0 1944 | 1945 | 1946 | 1947 | 1948 | -1 1949 | 1950 | 1951 | 1952 | 5 1953 | wxALL 1954 | 0 1955 | 1956 | 1 1957 | 1 1958 | 1 1959 | 1 1960 | 1961 | 1962 | 1963 | 1964 | 1965 | 1966 | 1967 | 1 1968 | 0 1969 | 1 1970 | 1971 | 1 1972 | 0 1973 | Dock 1974 | 0 1975 | Left 1976 | 1 1977 | 1978 | 1 1979 | 1980 | 0 1981 | 0 1982 | wxID_ANY 1983 | False Positive Text 1984 | 0 1985 | 1986 | 0 1987 | 1988 | 1989 | 0 1990 | 1991 | 1 1992 | m_staticText11 1993 | 1 1994 | 1995 | 1996 | protected 1997 | 1 1998 | 1999 | Resizable 2000 | 1 2001 | -1,20 2002 | 2003 | ; ; forward_declare 2004 | 0 2005 | 2006 | 2007 | 2008 | 2009 | -1 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 5 2016 | wxEXPAND 2017 | 1 2018 | 2019 | wxWRAPSIZER_DEFAULT_FLAGS 2020 | 2021 | wSizer7 2022 | wxHORIZONTAL 2023 | none 2024 | 2025 | 5 2026 | wxALL 2027 | 0 2028 | 2029 | 1 2030 | 1 2031 | 1 2032 | 1 2033 | 2034 | 2035 | 2036 | 2037 | 2038 | 2039 | 2040 | 1 2041 | 0 2042 | 1 2043 | 2044 | 1 2045 | 0 2046 | Dock 2047 | 0 2048 | Left 2049 | 1 2050 | 2051 | 1 2052 | 2053 | 0 2054 | 0 2055 | wxID_ANY 2056 | 2057 | 0 2058 | 2059 | 2060 | 2061 | 0 2062 | 2063 | 1 2064 | false_negative_content 2065 | 1 2066 | 2067 | 2068 | protected 2069 | 1 2070 | 2071 | Resizable 2072 | 1 2073 | 230,150 2074 | wxTE_MULTILINE 2075 | ; ; forward_declare 2076 | 0 2077 | 2078 | 2079 | wxFILTER_NONE 2080 | wxDefaultValidator 2081 | 2082 | 2083 | 2084 | 2085 | wxVSCROLL 2086 | 2087 | 2088 | 2089 | 5 2090 | wxALL 2091 | 0 2092 | 2093 | 1 2094 | 1 2095 | 1 2096 | 1 2097 | 2098 | 2099 | 2100 | 2101 | 2102 | 2103 | 2104 | 1 2105 | 0 2106 | 1 2107 | 2108 | 1 2109 | 0 2110 | Dock 2111 | 0 2112 | Left 2113 | 1 2114 | 2115 | 1 2116 | 2117 | 0 2118 | 0 2119 | wxID_ANY 2120 | 2121 | 0 2122 | 2123 | 2124 | 2125 | 0 2126 | 2127 | 1 2128 | false_positive_content 2129 | 1 2130 | 2131 | 2132 | protected 2133 | 1 2134 | 2135 | Resizable 2136 | 1 2137 | 230,150 2138 | wxTE_MULTILINE 2139 | ; ; forward_declare 2140 | 0 2141 | 2142 | 2143 | wxFILTER_NONE 2144 | wxDefaultValidator 2145 | 2146 | 2147 | 2148 | 2149 | wxVSCROLL 2150 | 2151 | 2152 | 2153 | 2154 | 2155 | 5 2156 | wxALIGN_RIGHT 2157 | 1 2158 | 2159 | wxWRAPSIZER_DEFAULT_FLAGS 2160 | 2161 | wSizer8 2162 | wxHORIZONTAL 2163 | none 2164 | 2165 | 5 2166 | wxALL 2167 | 0 2168 | 2169 | 1 2170 | 1 2171 | 1 2172 | 1 2173 | 2174 | 2175 | 2176 | 2177 | 2178 | 2179 | 2180 | 2181 | 1 2182 | 0 2183 | 1 2184 | 2185 | 1 2186 | 2187 | 0 2188 | 0 2189 | 2190 | Dock 2191 | 0 2192 | Left 2193 | 1 2194 | 2195 | 1 2196 | 2197 | 2198 | 0 2199 | 0 2200 | wxID_ANY 2201 | OK 2202 | 2203 | 0 2204 | 2205 | 0 2206 | 2207 | 2208 | 0 2209 | 2210 | 1 2211 | ok_but 2212 | 1 2213 | 2214 | 2215 | protected 2216 | 1 2217 | 2218 | 2219 | 2220 | Resizable 2221 | 1 2222 | 2223 | 2224 | ; ; forward_declare 2225 | 0 2226 | 2227 | 2228 | wxFILTER_NONE 2229 | wxDefaultValidator 2230 | 2231 | 2232 | 2233 | 2234 | ok_but_click 2235 | 2236 | 2237 | 2238 | 5 2239 | wxALL 2240 | 0 2241 | 2242 | 1 2243 | 1 2244 | 1 2245 | 1 2246 | 2247 | 2248 | 2249 | 2250 | 2251 | 2252 | 2253 | 2254 | 1 2255 | 0 2256 | 1 2257 | 2258 | 1 2259 | 2260 | 0 2261 | 0 2262 | 2263 | Dock 2264 | 0 2265 | Left 2266 | 1 2267 | 2268 | 1 2269 | 2270 | 2271 | 0 2272 | 0 2273 | wxID_ANY 2274 | Cancel 2275 | 2276 | 0 2277 | 2278 | 0 2279 | 2280 | 2281 | 0 2282 | 2283 | 1 2284 | cancel_but 2285 | 1 2286 | 2287 | 2288 | protected 2289 | 1 2290 | 2291 | 2292 | 2293 | Resizable 2294 | 1 2295 | 2296 | 2297 | ; ; forward_declare 2298 | 0 2299 | 2300 | 2301 | wxFILTER_NONE 2302 | wxDefaultValidator 2303 | 2304 | 2305 | 2306 | 2307 | cancel_but_click 2308 | 2309 | 2310 | 2311 | 2312 | 2313 | 2314 | 2315 | 2316 | --------------------------------------------------------------------------------