├── .gitattributes ├── .gitignore ├── README.md ├── auto-gui ├── click.py ├── sched.bat └── window.ini ├── build-instructions.md ├── elements.html ├── images ├── elements.png └── pw.png └── win32-server └── server.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![PEP8](https://img.shields.io/badge/code%20style-pep8-orange.svg)](https://www.python.org/dev/peps/pep-0008/) 3 | 4 | ### win32-server 5 | 6 | Simple python tool to observe all opened windows properties. 7 | 8 | ![Python Window Server](images/pw.png) 9 | 10 | ### auto-gui 11 | 12 | Python tool to automatically click on popups. Can be used with fuzzing 13 | tools. 14 | 15 | List of files: 16 | * click.py - tool itself 17 | * sched.bat - bat file that automates click.py launch ever 120 seconds 18 | * window.ini - configuration file (you can add own windows) 19 | 20 | Requires pywin32. 21 | 22 | ### elements.html 23 | 24 | This is a simple and ugly script to enumerate elements properties 25 | (or HTML*Element or HTMLElement or Element or Node or whatever). 26 | 27 | Attributes are clickable, you will see contents in console.log, also 28 | you can hide blocks. 29 | 30 | ![Elements](images/elements.png) 31 | -------------------------------------------------------------------------------- /auto-gui/click.py: -------------------------------------------------------------------------------- 1 | #!c:\\python27\python.exe 2 | # -*- coding: utf-8 -*- 3 | # pylint: disable=E1101 4 | # pylint: disable=C0103 5 | 6 | """Tool to close popups.""" 7 | 8 | import ConfigParser 9 | import win32gui 10 | import win32api 11 | import win32con 12 | import win32process 13 | import ctypes 14 | 15 | __version__ = "0.0.2" 16 | __author__ = "Arthur Gerkis" 17 | 18 | 19 | # http://www.brunningonline.net/simon/blog/archives/000664.html 20 | TH32CS_SNAPPROCESS = 0x00000002 21 | 22 | 23 | class PROCESSENTRY32(ctypes.Structure): 24 | """PROCESSENTRY32 structure.""" 25 | _fields_ = [("dwSize", ctypes.c_ulong), 26 | ("cntUsage", ctypes.c_ulong), 27 | ("th32ProcessID", ctypes.c_ulong), 28 | ("th32DefaultHeapID", ctypes.c_ulong), 29 | ("th32ModuleID", ctypes.c_ulong), 30 | ("cntThreads", ctypes.c_ulong), 31 | ("th32ParentProcessID", ctypes.c_ulong), 32 | ("pcPriClassBase", ctypes.c_ulong), 33 | ("dwFlags", ctypes.c_ulong), 34 | ("szExeFile", ctypes.c_char * 260)] 35 | 36 | 37 | class GUIController(object): 38 | """GUIController class""" 39 | def __init__(self): 40 | self._text_to_act_hash = {} 41 | self._allowed_proc = ['firefox.exe', 'iexplore.exe', 'chrome.exe'] 42 | self._allowed_actions = ['cancel', 'ok', 'close'] 43 | self._pids = [] 44 | self._read_config() 45 | 46 | def _read_config(self): 47 | """Read configuration file.""" 48 | config = ConfigParser.ConfigParser() 49 | config.optionxform = str 50 | config.read('window.ini') 51 | sections = config.sections() 52 | for item in sections: 53 | ary = config.items(item) 54 | for i in ary: 55 | self._text_to_act_hash[i[0]] = (item, i[1]) 56 | return 57 | 58 | def rand_clicks(self): 59 | """Trigger random clicks.""" 60 | self._enumerate('clicks') 61 | return 62 | 63 | def close_popups(self): 64 | """Close popups.""" 65 | self._enumerate('popups') 66 | return 67 | 68 | def resize(self): 69 | """Resize window.""" 70 | self._enumerate('resize') 71 | return 72 | 73 | def _enumerate(self, a_type): 74 | """Enumerate all handles.""" 75 | 76 | self._enum_proc() 77 | win32gui.EnumChildWindows(None, self._enum_callback, a_type) 78 | self._pids = [] 79 | return 80 | 81 | def _enum_callback(self, hwnd, param=None): 82 | """Enumerate callbacks.""" 83 | win_text = win32gui.GetWindowText(hwnd) 84 | class_name = win32gui.GetClassName(hwnd) 85 | _, pid = win32process.GetWindowThreadProcessId(hwnd) 86 | 87 | if not self._pid_belongs(pid) or win_text == '': 88 | return 89 | 90 | if param == 'resize': 91 | (_, _, w, h) = self._get_coords(hwnd) 92 | self._resize(hwnd, 800, 400) 93 | print "Resize" 94 | return 95 | 96 | if param == 'clicks': 97 | _, _, w, h = self._get_coords(hwnd) 98 | self._click_pos(hwnd, (w + 30, h + 30)) 99 | print "Clicks" 100 | return 101 | 102 | found = False 103 | for key in self._text_to_act_hash.keys(): 104 | (cls, action) = self._text_to_act_hash[key] 105 | action = action.strip("'") 106 | if class_name != cls: 107 | continue 108 | if win_text.find(key) != 0: 109 | continue 110 | found = True 111 | 112 | if not found: 113 | return 114 | if action not in self._allowed_actions: 115 | return 116 | 117 | if action == 'close': 118 | print 'CLOSE' 119 | self._close(hwnd) 120 | 121 | elif action == 'ok': 122 | print 'OK' 123 | self._click_ok(hwnd) 124 | 125 | elif action == 'cancel': 126 | print 'CANCEL' 127 | self._click_cancel(hwnd) 128 | return 129 | 130 | @classmethod 131 | def _close(cls, hwnd): 132 | """Send close message.""" 133 | 134 | win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0) 135 | return 136 | 137 | def _click_cancel(self, hwnd): 138 | """Click Cancel button.""" 139 | button = win32gui.FindWindowEx(hwnd, 0, "Button", "Cancel") 140 | if button: 141 | print 'Button Cancel =', button 142 | self._click(button) 143 | return 144 | 145 | def _click_ok(self, hwnd): 146 | """Click Ok button.""" 147 | button = win32gui.FindWindowEx(hwnd, 0, "Button", "OK") 148 | if button: 149 | print 'Button OK =', button 150 | self._click(button) 151 | 152 | button = win32gui.FindWindowEx(hwnd, 0, "Button", "&Yes") 153 | if button: 154 | print 'Button Yes =', button 155 | self._click(button) 156 | return 157 | 158 | @classmethod 159 | def _click_pos(cls, hwnd, pos=None): 160 | """Send click message.""" 161 | tmp = 0 162 | if pos: 163 | client_pos = win32gui.ScreenToClient(hwnd, pos) 164 | tmp = win32api.MAKELONG(client_pos[0], client_pos[1]) 165 | win32gui.SendMessage(hwnd, 166 | win32con.WM_ACTIVATE, 167 | win32con.WA_ACTIVE, 0) 168 | win32gui.SendMessage(hwnd, 169 | win32con.WM_LBUTTONDOWN, 170 | win32con.MK_LBUTTON, tmp) 171 | win32gui.SendMessage(hwnd, 172 | win32con.WM_LBUTTONUP, 173 | win32con.MK_LBUTTON, tmp) 174 | return 175 | 176 | @classmethod 177 | def _resize(cls, hwnd, new_x, new_y, new_w=800, new_h=400): 178 | """Resize window.""" 179 | hwout = None 180 | win32gui.SetWindowPos(hwnd, hwout, new_x, new_y, new_w, new_h, 0x0040) 181 | return 182 | 183 | def _pid_belongs(self, pid): 184 | """Detect if PID belongs to PIDs.""" 185 | if pid in self._pids: 186 | return True 187 | return False 188 | 189 | def _enum_proc(self): 190 | """Enumerate processes.""" 191 | # See http://msdn2.microsoft.com/en-us/library/ms686701.aspx 192 | hProcessSnap = ctypes.windll.kernel32.\ 193 | CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 194 | pe32 = PROCESSENTRY32() 195 | pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) 196 | if ctypes.windll.kernel32.Process32First(hProcessSnap, 197 | ctypes.byref(pe32)) == \ 198 | win32con.FALSE: 199 | print "Failed getting first process." 200 | return 201 | while True: 202 | if pe32.szExeFile in self._allowed_proc: 203 | self._pids.append(pe32.th32ProcessID) 204 | if ctypes.windll.kernel32.Process32Next(hProcessSnap, 205 | ctypes.byref(pe32)) == \ 206 | win32con.FALSE: 207 | break 208 | ctypes.windll.kernel32.CloseHandle(hProcessSnap) 209 | return 210 | 211 | @classmethod 212 | def _click(cls, hwnd): 213 | """Click.""" 214 | win32gui.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, 0, 0) 215 | win32gui.SendMessage(hwnd, win32con.WM_LBUTTONUP, 0, 0) 216 | return 217 | 218 | @classmethod 219 | def _get_coords(cls, hwnd): 220 | """Change width/height of window.""" 221 | rect = win32gui.GetWindowRect(hwnd) 222 | x = rect[0] 223 | y = rect[1] 224 | w = rect[2] - x 225 | h = rect[3] - y 226 | return (x, y, w, h) 227 | 228 | 229 | def main(): 230 | """Main.""" 231 | gctrl = GUIController() 232 | gctrl.close_popups() 233 | 234 | if __name__ == '__main__': 235 | main() 236 | -------------------------------------------------------------------------------- /auto-gui/sched.bat: -------------------------------------------------------------------------------- 1 | :label 2 | python ./click.py 3 | timeout 120 4 | goto label 5 | -------------------------------------------------------------------------------- /auto-gui/window.ini: -------------------------------------------------------------------------------- 1 | [MozillaDialogClass] 2 | Warning: Unresponsive = 'close' 3 | Opening = 'close' 4 | Launch Application = 'close' 5 | Download Error = 'close' 6 | Find in This Page = 'close' 7 | Install Error = 'close' 8 | Confirm = 'close' 9 | Opening = 'close' 10 | 11 | [CSpNotify Notify Window] 12 | CSpNotify Notify Window = 'close' 13 | 14 | [GLContextWGLClass] 15 | G = 'close' 16 | 17 | [MozillaWindowClass] 18 | Problem loading page = 'close' 19 | trailer.mp4 = 'close' 20 | 21 | [Internet Explorer_TridentDlgFrame] 22 | Script Error = 'close' 23 | Picture = 'close' 24 | 25 | [#32270] 26 | Windows Internet Explorer = 'close' 27 | Message from webpage = 'close' 28 | InPrivate Downloads = 'close' 29 | http:// = 'close' 30 | Choose File to Upload = 'close' 31 | Security alert = 'close' 32 | Email = 'close' 33 | 34 | [TabThumbnailWindow] 35 | 403 Forbidden = 'close' 36 | Blank Page = 'close' 37 | HTTP 404 Not Found = 'close' 38 | 39 | [IEFrame] 40 | Hedgehog - the = 'close' -------------------------------------------------------------------------------- /build-instructions.md: -------------------------------------------------------------------------------- 1 | Instructions to build interpreters with AddressSanitizer 2 | -------------------------------------------------------- 3 | 4 | You can actually build not only with AddressSanitizer, there are more options 5 | to choose from: http://clang.llvm.org/docs/index.html. Also I prefer Clang. 6 | 7 | Update clang to the latest available version: 8 | ``` 9 | $ sudo apt-get install clang-3.4 10 | ``` 11 | or 12 | ``` 13 | $ sudo apt-get install clang-3.5 14 | ``` 15 | 16 | Add symbolizer path: 17 | ``` 18 | $ export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4 19 | ``` 20 | 21 | ## Building PHP 22 | 23 | Download PHP source code, extract. 24 | 25 | Install build dev tools for PHP: 26 | ``` 27 | $ sudo apt-get build-dep php5 28 | $ sudo apt-get -y install libfcgi-dev libfcgi0ldbl libjpeg62-dbg libmcrypt-dev libssl-dev 29 | 30 | $ export CC="/usr/bin/clang -fsanitize=address -fsanitize-blacklist=blacklist.txt" 31 | $ export CXX="/usr/bin/clang++ -fsanitize=address" 32 | $ configure (whatever you want...) --disable-phar 33 | $ make 34 | ``` 35 | 36 | Contents of blacklist.txt: 37 | ``` 38 | src:Zend/zend_hash.c 39 | ``` 40 | 41 | Make sure you provide correct paths, otherwise blacklist will not apply. 42 | 43 | Blacklist file "blacklist.txt" and "--disable-phar" are required because PHP 44 | will crash either during build time, or right upon start. 45 | 46 | After build your binary will be located in: ./sapi/cli/php 47 | 48 | 49 | ## Building Perl 50 | 51 | Download Perl source code, extract. 52 | 53 | ``` 54 | $ ./Configure -des -Doptimize="-g -O1" -DEBUGGING=both -Accflags=-fsanitize=address -Aldflags=-fsanitize=address -Alddlflags=-fsanitize=address -Dusethreads -Dusemultiplicity -Dusesitecustomize -Dusedevel -Uversiononly -Dcc=clang 55 | $ make 56 | $ make (second make required when makefile has been changed) 57 | ``` 58 | 59 | I recommend running build first without -fsanitize=address to avoid build 60 | issues. After first dry-run you can build with AddressSanitizer. 61 | 62 | After build your binary will be located in: ./perl 63 | 64 | 65 | ## Building Python 66 | 67 | https://docs.python.org/devguide/clang.html#building-python 68 | 69 | After build your binary will be located in: ./Python 70 | 71 | ## Notes 72 | 73 | I have been building on Ubuntu 12.0, clang 3.4., also clean Ubuntu 14.0, clang 3.4. 74 | -------------------------------------------------------------------------------- /elements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inheritance and attributes table 6 | 185 | 186 | 248 | 249 | 250 | 251 |

Inheritance and attributes table

252 | 253 |
254 |
255 | HTML 256 |
257 |
258 | 259 |
260 | SVG 261 |
262 |
263 | 264 |
265 | MathML 266 |
267 |
268 |
269 | 270 |
271 |
272 | Custom 273 |
274 | Element name 275 |
276 | http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul 277 |
278 | http://www.w3.org/1999/xhtml 279 |
280 | http://www.w3.org/2000/svg 281 |
282 | http://www.w3.org/1999/xlink 283 |
284 | http://www.w3.org/1999/XSL/Transform 285 |
286 | http://www.w3.org/1998/Math/MathML 287 |
288 | null 289 |
290 |
291 | 292 |
293 |
294 |
295 |
296 |
297 |
298 | 299 | 300 | -------------------------------------------------------------------------------- /images/elements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/misc/b88ae56019eb9ddd9f62e51d1f729abc8387e609/images/elements.png -------------------------------------------------------------------------------- /images/pw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ax330d/misc/b88ae56019eb9ddd9f62e51d1f729abc8387e609/images/pw.png -------------------------------------------------------------------------------- /win32-server/server.py: -------------------------------------------------------------------------------- 1 | #!c:\\python27\python.exe 2 | # -*- coding: utf-8 -*- 3 | # pylint: disable=E1101 4 | 5 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 6 | from SocketServer import ThreadingMixIn 7 | import threading 8 | import cgi 9 | # FIXME: remove win32 dep 10 | import win32gui 11 | import win32con 12 | import win32process 13 | import ctypes 14 | 15 | 16 | __version__ = "0.0.2" 17 | __author__ = "Arthur Gerkis" 18 | 19 | 20 | # http://www.brunningonline.net/simon/blog/archives/000664.html 21 | TH32CS_SNAPPROCESS = 0x00000002 22 | 23 | 24 | class PROCESSENTRY32(ctypes.Structure): 25 | _fields_ = [("dwSize", ctypes.c_ulong), 26 | ("cntUsage", ctypes.c_ulong), 27 | ("th32ProcessID", ctypes.c_ulong), 28 | ("th32DefaultHeapID", ctypes.c_ulong), 29 | ("th32ModuleID", ctypes.c_ulong), 30 | ("cntThreads", ctypes.c_ulong), 31 | ("th32ParentProcessID", ctypes.c_ulong), 32 | ("pcPriClassBase", ctypes.c_ulong), 33 | ("dwFlags", ctypes.c_ulong), 34 | ("szExeFile", ctypes.c_char * 260)] 35 | 36 | 37 | class HTTPRequestHandler(BaseHTTPRequestHandler): 38 | """Class to handle HTTP requests.""" 39 | def __init__(self, *args): 40 | BaseHTTPRequestHandler.__init__(self, *args) 41 | 42 | @classmethod 43 | def _get_coords(cls, hwnd): 44 | """Get dimensions of window.""" 45 | 46 | rect = win32gui.GetWindowRect(hwnd) 47 | xdim = rect[0] 48 | ydim = rect[1] 49 | wdim = rect[2] - xdim 50 | hdim = rect[3] - ydim 51 | return (xdim, ydim, wdim, hdim) 52 | 53 | def _enum_callback(self, handle, param): 54 | """Processes enumeration callack.""" 55 | 56 | win_text = cgi.escape(win32gui.GetWindowText(handle)) 57 | class_name = cgi.escape(win32gui.GetClassName(handle)) 58 | _, pid = win32process.GetWindowThreadProcessId(handle) 59 | (xdim, ydim, wdim, hdim) = self._get_coords(handle) 60 | if pid not in param: 61 | param[pid] = [] 62 | param[pid].append((handle, class_name, win_text, xdim, 63 | ydim, wdim, hdim)) 64 | 65 | classes = ["Button", "ComboBox", "Edit", "ListBox", "MDIClient", 66 | "ScrollBar", "Static", "ComboLBox", "DDEMLEvent", "Message", 67 | "#32768", "#32769", "#32770", "#32771", "#32772"] 68 | for _class in classes: 69 | button = win32gui.FindWindowEx(handle, 0, _class, None) 70 | if not button: 71 | continue 72 | text = win32gui.GetWindowText(button) 73 | print "class = {}, button = {:x}, PID = {}, text = {}".\ 74 | format(_class, button, pid, text) 75 | return 76 | 77 | def _call_win32(self): 78 | """Enumerate processes.""" 79 | 80 | pid_group = {} 81 | proc_names = {} 82 | data = "" 83 | counter = 0 84 | 85 | self._enum_proc(proc_names) 86 | win32gui.EnumChildWindows(None, self._enum_callback, pid_group) 87 | 88 | line = ("" 89 | " {nr}" 90 | " {handle:x}" 91 | " {class_name}" 92 | " {win_text}" 93 | " {dimension}" 94 | "\n") 95 | 96 | for _pid in sorted(pid_group): 97 | name = "?" 98 | if _pid in proc_names: 99 | name = proc_names[_pid] 100 | data += ("" 101 | " PID: {pid}" 102 | " Name: {name}" 103 | "\n").format(pid=_pid, name=name) 104 | for _epid in pid_group[_pid]: 105 | handle, class_name, win_text, xdim, ydim, wdim, hdim = _epid 106 | dimension = "x = {}, y = {}, {} x {}".\ 107 | format(xdim, ydim, wdim, hdim) 108 | data += line.format(nr=counter, handle=handle, 109 | class_name=class_name, 110 | win_text=win_text, 111 | dimension=dimension) 112 | counter += 1 113 | return data 114 | 115 | @classmethod 116 | def _enum_proc(cls, proc_names): 117 | """Enumerate processes, get PIDs and names.""" 118 | 119 | # See http://msdn2.microsoft.com/en-us/library/ms686701.aspx 120 | hProcessSnap = ctypes.windll.kernel32.\ 121 | CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 122 | pe32 = PROCESSENTRY32() 123 | pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) 124 | if ctypes.windll.kernel32.Process32First(hProcessSnap, 125 | ctypes.byref(pe32)) == \ 126 | win32con.FALSE: 127 | print "Failed getting first process." 128 | return 129 | while True: 130 | proc_names[pe32.th32ProcessID] = pe32.szExeFile 131 | if ctypes.windll.kernel32.Process32Next(hProcessSnap, 132 | ctypes.byref(pe32)) == \ 133 | win32con.FALSE: 134 | break 135 | ctypes.windll.kernel32.CloseHandle(hProcessSnap) 136 | return 137 | 138 | def end_response(self, html): 139 | """End request response.""" 140 | 141 | self.send_response(200) 142 | self.send_header("Content-type", "text/html") 143 | self.end_headers() 144 | self.wfile.write(html) 145 | 146 | def do_GET(self): 147 | 148 | template = """ 149 | 150 | 151 | 152 | Python Window explorer 153 | 154 | 181 | 182 | 183 | 184 |

Python Window explorer

185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | {table} 194 |
Nr.HandleClass nameWindow textDimensions
195 | 196 | 197 | """ 198 | 199 | short_path = "" 200 | if self.path.find("?") != -1: 201 | short_path, query_string = self.path.split("?", 1) 202 | _ = query_string.split("&") 203 | else: 204 | short_path = self.path 205 | 206 | if short_path == "/": 207 | data = self._call_win32() 208 | html = template.format(table=data) 209 | self.end_response(html) 210 | return 211 | 212 | 213 | class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 214 | """Implements threaded HTTP server.""" 215 | allow_reuse_address = True 216 | 217 | def shutdown(self): 218 | self.socket.close() 219 | HTTPServer.shutdown(self) 220 | 221 | 222 | class SimpleHttpServer(object): 223 | """Simple threaded HTTP server.""" 224 | def __init__(self, ip_addr, port): 225 | super(SimpleHttpServer, self).__init__() 226 | self.server_thread = None 227 | 228 | def handler(*args): 229 | """Wrap handler.""" 230 | HTTPRequestHandler(*args) 231 | self.server = ThreadedHTTPServer((ip_addr, port), handler) 232 | 233 | def start(self): 234 | """Start server daemon.""" 235 | self.server_thread = threading.Thread(target=self.server.serve_forever) 236 | self.server_thread.daemon = True 237 | self.server_thread.start() 238 | 239 | def wait_for_thread(self): 240 | """Wait for thread.""" 241 | self.server_thread.join() 242 | 243 | def stop(self): 244 | """Stop server.""" 245 | self.server.shutdown() 246 | self.wait_for_thread() 247 | 248 | 249 | def main(): 250 | """Entry point.""" 251 | server = SimpleHttpServer("", 9090) 252 | print "HTTP Server Running..." 253 | server.start() 254 | server.wait_for_thread() 255 | 256 | 257 | if __name__ == "__main__": 258 | main() 259 | --------------------------------------------------------------------------------