#!/usr/bin/env python # -*- coding: utf-8 -*- # wxGlade powered import wx import serial import threading import time from serialEvent import * from settingsDialog import SerialDialog from displayDialog import DisplayDialog import time from globals import * ID_CONNECT = wx.NewId() ID_DISCONNECT = wx.NewId() ID_PORT_SETTINGS = wx.NewId() ID_DISPLAY_SETTINGS = wx.NewId() outputCnt = 0 def parseInitFile(f, serial, settings): d = [line[:-1].strip() for line in f.readlines()] #parse not comment starts with # and [serial] information a = [line for line in d if line != '' and line[0] != '[' and line[0] != '#'] ee = [c.split('#',1)[0].strip().split('=') for c in a] qq = [(a[0].strip(),a[1].strip()) for a in ee] for name, value in qq: #parity='N' or'O' can not be eval() if name == 'parity': serial.parity = value #sometimes port = '/dev/ttyS0' elif name == 'port': try: serial.port = int(value) except: serial.port = value #font format as "fontname-pointsize" as "Courier 10 Pitch-10" elif name == 'font': x,y = value.split('-') #utf-8 is always our choice :D try: settings.font = wx.Font(eval(y), wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, x) except UnicodeDecodeError: settings.font = wx.Font(eval(y), wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, x.decode('utf-8')) #for other attrs, we just set there attr by setattr and got their value by eval() else: if hasattr(serial, name): setattr(serial,name, eval(value)) else: setattr(settings,name, eval(value)) def writeInitFile(f, serial, settings): serial_para = ['port', 'baudrate', 'bytesize', 'stopbits', 'parity', 'rtscts', 'xonxoff'] settings_para = ['echo', 'newline', 'unprintable', 'forecolor', 'backcolor'] f.write('[serial]\n') for item in serial_para: f.write('%s = %s\n'%(item, str(getattr(serial, item)))) f.write('[settings]\n') for item in settings_para: f.write('%s = %s\n'%(item, str(getattr(settings, item)))) #we need to process the font class before we write try: f.write('font = %s-%d\n' % (settings.font.GetFaceName(), settings.font.GetPointSize())) except UnicodeEncodeError: # utf-8 will be all right? f.write('font = %s-%d\n' % (settings.font.GetFaceName().encode('utf-8'), settings.font.GetPointSize())) class TerminalSettings: """for the display setting of the terminal """ def __init__(self, echo = False, unprintable = True, newline = CRLF): self.echo = echo self.unprintable = unprintable self.newline = newline self.forecolor = None self.backcolor = None self.font = None class ComThread(threading.Thread): """Read the serial port continously in this thread, timeout sets to 0.3s""" def __init__(self, parent): threading.Thread.__init__(self) self.parent = parent self.serial = self.parent.serial self.alive = threading.Event() self.alive.set() self.setDaemon(1) def run(self): while(self.alive.isSet()): #print 'thread run...' text = self.serial.read(1) #read one, with timout if text: #check if not timeout n = self.serial.inWaiting() #look if there is more to read if n: text = text + self.serial.read(n) #get it #newline transformation if self.parent.settings.newline == CR: text = text.replace('\r', '\n') elif self.parent.settings.newline == LF: pass elif self.parent.settings.newline == CRLF: text = text.replace('\r\n', '\n') # #this is an elegant way to update the GUI in the not-main-thread event = SerialRxEvent(self.parent.GetId(), text) self.parent.GetEventHandler().AddPendingEvent(event) def stopRun(self): self.alive.clear() class FrameTerminal(wx.Frame): """main frame of the serial assist """ def __init__(self, output_window, *args, **kwds): # begin wxGlade: FrameTerminal.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) self.frame_statusbar = self.CreateStatusBar(1, 0) self.outputwin = output_window self.linebuffer = "" self.currentchar = "" self.inputdata = "" self.linebufferlist = [] # Tool Bar self.frame_toolbar = wx.ToolBar(self, -1) self.SetToolBar(self.frame_toolbar) path = self.outputwin.workingdir self.frame_toolbar.AddLabelTool(ID_CONNECT, "connect", wx.Bitmap(path + "/../images/play.png", wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "Connect to serial port", "Connect to serial port") self.frame_toolbar.AddLabelTool(ID_DISCONNECT, "disconnect", wx.Bitmap(path + "/../images/stop.png", wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "Disconnect from serial port", "Disconnect from serial port") self.frame_toolbar.AddLabelTool(ID_PORT_SETTINGS, "SerialportSettings", wx.Bitmap(path + "/../images/configure.png", wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "Port settings", "Port settings") self.frame_toolbar.AddLabelTool(ID_DISPLAY_SETTINGS, "DisplaySettings", wx.Bitmap(path + "/../images/configure_display.png", wx.BITMAP_TYPE_ANY), wx.NullBitmap, wx.ITEM_NORMAL, "Display settings", "Display settings") # Tool Bar end self.text_ctrl_output = wx.TextCtrl(self, -1, "", style=wx.TE_PROCESS_TAB|wx.TE_MULTILINE |wx.TE_READONLY|wx.HSCROLL|wx.TE_RICH2 |wx.TE_LINEWRAP) self.__set_properties() self.__do_layout() self.Bind(wx.EVT_TOOL, self.onConnect, id=ID_CONNECT) self.Bind(wx.EVT_TOOL, self.onDisconnect, id=ID_DISCONNECT) self.Bind(wx.EVT_TOOL, self.onPortSettings, id=ID_PORT_SETTINGS) self.Bind(wx.EVT_TOOL, self.onDisplaySettings, id=ID_DISPLAY_SETTINGS) # end wxGlade self.settings = TerminalSettings() #init display settings self.serial = serial.Serial() self.outputwin.ser = self.serial self.comThread = None self.title = DEFAULT_TITLE self.SetTitle(self.title) self.f = None #file for autosaving self.settings.font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, u'Courier 10 Pitch') self.text_ctrl_output.Bind(wx.EVT_CHAR, self.onChar, id = -1) self.Bind(EVT_SERIALRX, self.onSerialRead) self.Bind(wx.EVT_CLOSE, self.onClose) wx.CallLater(AUTOSAVE_PERIOD, self.autoSave) self.frame_toolbar.EnableTool(ID_DISCONNECT, False) self.preData = WELCOME_INFO #used for autosave to diff if new character are there D_DEFAULT_FONT = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, u'Courier 10 Pitch') self.__parse_initial() self.updateTextctrl() self.light = -2 self.text_ctrl_output.WriteText(WELCOME_INFO) def __set_properties(self): # begin wxGlade: FrameTerminal.__set_properties #self.SetTitle("serialAssist") _icon = wx.EmptyIcon() path = self.outputwin.workingdir _icon.CopyFromBitmap(wx.Bitmap(path + "/../images/zulu_logo16x16.png", wx.BITMAP_TYPE_ANY)) self.SetIcon(_icon) #self.SetSize((543, 433)) self.SetSize((743, 560)) self.frame_statusbar.SetStatusWidths([-1]) # statusbar fields frame_statusbar_fields = [""] for i in range(len(frame_statusbar_fields)): self.frame_statusbar.SetStatusText(frame_statusbar_fields[i], i) self.frame_toolbar.SetToolBitmapSize((32, 32)) self.frame_toolbar.SetToolSeparation(9) self.frame_toolbar.Realize() self.text_ctrl_output.SetFont(wx.Font(11, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "")) # end wxGlade def __do_layout(self): # begin wxGlade: FrameTerminal.__do_layout sizer_5 = wx.BoxSizer(wx.VERTICAL) sizer_5.Add(self.text_ctrl_output, 2, wx.ALL|wx.EXPAND, 10) self.SetSizer(sizer_5) self.Layout() # end wxGlade def __parse_initial(self): path = self.outputwin.workingdir try: f = open(path + '/serial_init.txt', 'r') parseInitFile(f, self.serial, self.settings) f.close() #if load init file fails, we reload the defaults except Exception, e: print e self.serial.port = S_PORT self.serial.baudrate = S_BAUDRATE self.serial.bytesize = S_BYTESIZE self.serial.stopbits = S_STOPBITS self.serial.parity = S_PARITY self.serial.rtscts = S_RTSCTS self.serial.xonxoff = S_XONXOFF self.settings.echo = D_ECHO self.settings.newline = D_NEWLINE self.settings.unprintable = D_UNPRINTABLE self.settings.forecolor = D_FOREGROUND_COLOR self.settings.backcolor = D_BACKGROUND_COLOR self.settings.font = wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, u'Courier 10 Pitch') self.outputwin.ser = self.serial def updateTextctrl(self): """update the output text ctrl by diplay settings """ if self.settings.forecolor is not None: self.text_ctrl_output.SetForegroundColour(self.settings.forecolor) if self.settings.backcolor is not None: self.text_ctrl_output.SetBackgroundColour(self.settings.backcolor) if self.settings.font is not None: self.text_ctrl_output.SetFont(self.settings.font) text = self.text_ctrl_output.GetValue() self.text_ctrl_output.Clear() self.text_ctrl_output.SetValue(text) self.text_ctrl_output.Update() # print data to widget text_ctrl_ouptput def printt(self, data): """print to text control(terminal) """ self.text_ctrl_output.AppendText(data) def printTitle(self, data): """information stay as title for 1.8 seconds """ #try: # wx.CallLater(1800, lambda x: x.SetTitle(self.title), self) #except: # return #self.SetTitle(data) pass def printStatusbar(self, data): """information stay at statusbar for 1.8 seconds """ try: #wx.CallLater(1800, lambda x: x.SetStatusText(''), self.frame_statusbar) self.frame_statusbar.SetStatusText(data) except: pass def autoSave(self): """auto save callback function for saving the serial output and input """ data = self.text_ctrl_output.GetValue() if data != self.preData: path = self.outputwin.workingdir + "/../logs/serial_log" + time.strftime("_%Y-%m-%d_%H-%M-%S", time.localtime()) + ".log" self.f = open(path, 'w') self.f.write('recent saved at : %s\n' % time.asctime()) self.f.write(data) self.f.close() self.preData = data wx.CallLater(AUTOSAVE_PERIOD, self.autoSave) def connect(self): status = False try: self.serial.timeout = 0.3 #set serial polling timeout to 0.3s. if self.serial.port is None: self.serial.port = 0 status = self.serial.open() self.title = "Serial Terminal on %s [%s, %s%s%s%s%s]" % ( self.serial.portstr, self.serial.baudrate, self.serial.bytesize, self.serial.parity, self.serial.stopbits, self.serial.rtscts and ' RTS/CTS' or '', self.serial.xonxoff and ' Xon/Xoff' or '', ) self.SetTitle(self.title) except Exception, e: dlg = wx.MessageDialog(self,'exception occured: %s, please check your configuration'%str(e), style = wx.OK|wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.SetTitle(DEFAULT_TITLE) return if status is False: self.printTitle('choose the right com for connecting~') return self.printTitle('connection build...') self.frame_toolbar.EnableTool(ID_DISCONNECT, True) self.frame_toolbar.EnableTool(ID_CONNECT, False) self.comThread = ComThread(self) self.comThread.start() def onConnect(self, event): # wxGlade: FrameTerminal.<event_handler> self.connect() def disconnect(self): if self.comThread is not None: self.comThread.stopRun() self.comThread.join() self.comThread = None self.printTitle('disconnect success...') self.serial.close() #it doesn't matter if you call close when serial was closed self.title = DEFAULT_TITLE self.frame_toolbar.EnableTool(ID_DISCONNECT, False) self.frame_toolbar.EnableTool(ID_CONNECT, True) def onDisconnect(self, event): # wxGlade: FrameTerminal.<event_handler> #self.disconnect() self.onClose(1) def onPortSettings(self, event): # wxGlade: FrameTerminal.<event_handler> self.disconnect() serial_dialog = SerialDialog(self.outputwin, self, -1, "", serial = self.serial) status = serial_dialog.ShowModal() serial_dialog.Destroy() #self.connect() def onDisplaySettings(self, event): # wxGlade: FrameTerminal.<event_handler> serial_dialog = DisplayDialog(self.outputwin, self, -1, "", parent = self) serial_dialog.ShowModal() serial_dialog.Destroy() def onSerialRead(self, event): global outputCnt text = event.data if text != "\x0d" and text != "\x0a": self.inputdata += text if (text == "\x0d" or text == "\x0a") and len(self.inputdata) > 0: tmplist = [] tmplist.append("127.0.0.2") tmplist.append(20) self.outputwin.packets.append([tmplist,self.inputdata]) self.inputdata = "" if self.settings.unprintable: for i in text: outputCnt+=1 if outputCnt%OUTPUT_PERLINE == 0: self.printt('\n') self.printt(repr(i)[1:-1]) else: for i in text: if i == '\b': beg = self.text_ctrl_output.GetLastPosition() end = beg - 1 self.text_ctrl_output.Remove(beg, end) else: self.printt(i) def onClose(self, event): if self.comThread is not None: self.comThread.stopRun() self.comThread.join() self.serial.close() #we must ensure the file was closed before exit. if self.f is not None: self.f.close() f = open('serial_init.txt', 'w') writeInitFile(f, self.serial, self.settings) f.close() self.outputwin.serial_capture = True self.outputwin.process_input_data() self.outputwin.mb.Enable(self.outputwin.ID_Start_Capture, True) self.outputwin.mb.Enable(self.outputwin.ID_Configure_Proxy, True) self.outputwin.mb.Enable(self.outputwin.ID_Open_Session, True) self.outputwin.mb.Enable(self.outputwin.ID_Save_Session, True) self.outputwin.mb.Enable(self.outputwin.ID_Configure_Logfile, True) self.outputwin.mb.Enable(self.outputwin.ID_Configure_Email, True) self.outputwin.mb.Enable(self.outputwin.ID_Import_PCAP, True) self.outputwin.mb.Enable(self.outputwin.ID_Start_Serial_Capture, True) self.outputwin.mb.Enable(self.outputwin.ID_Import_USB, True) self.outputwin.mb.Enable(self.outputwin.ID_Configure_VMware, True) self.outputwin.mb.Enable(self.outputwin.ID_Network_Fuzzer, True) self.outputwin.mb.Enable(self.outputwin.ID_File_Fuzzer, True) self.outputwin.mb.Enable(self.outputwin.ID_USB_Fuzzer, True) self.outputwin.mb.Enable(self.outputwin.ID_Serial_Fuzzer, True) self.outputwin.mb.Enable(self.outputwin.ID_Import_File, True) self.outputwin.tb.EnableTool(self.outputwin.ID_toolProxyStart, True) self.outputwin.tb.EnableTool(self.outputwin.ID_toolConfigure, True) self.outputwin.tb.EnableTool(self.outputwin.ID_toolOpenFile, True) self.outputwin.tb.EnableTool(self.outputwin.ID_toolSaveFile, True) self.outputwin.tb.EnableTool(self.outputwin.ID_toolFindNext, True) self.Destroy() def onChar(self, event): char = event.GetKeyCode() if char == 19: #Ctrl+S connect to serial port if self.light == -2: self.connect() self.light = -1 return elif char == 4: if self.light == -1: self.disconnect() #Ctrl+D disconnect to the serial port self.light = -2 return else: if self.serial.isOpen() is False: self.printTitle('You should connect to the serial port first~') return if chr(char) == '\r': #'\r' is when the return key is pressed. self.linebuffer += chr(char) tmplist = [] tmplist.append("127.0.0.1") tmplist.append(10) self.outputwin.packets.append([tmplist,self.linebuffer]) self.linebuffer = "" #print self.outputwin.packets if self.settings.newline == CR: self.serial.write('\r') elif self.settings.newline == LF: self.serial.write('\n') elif self.settings.newline == CRLF: self.serial.write('\r\n') if self.settings.echo == True: self.printt('\n') else: self.serial.write(chr(char)) self.linebuffer += chr(char) self.currentchar = chr(char) if self.settings.echo == True: if chr(char) == '\b': beg = self.text_ctrl_output.GetLastPosition() end = beg - 1 self.text_ctrl_output.Remove(beg, end) else: self.printt(chr(char)) # end of class FrameTerminal if __name__ == "__main__": app = wx.PySimpleApp(0) wx.InitAllImageHandlers() frame_terminal = FrameTerminal(None, -1, "") app.SetTopWindow(frame_terminal) frame_terminal.Show() app.MainLoop()