Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | # A module to expose various thread/process/job related structures and |
michael@0 | 2 | # methods from kernel32 |
michael@0 | 3 | # |
michael@0 | 4 | # The MIT License |
michael@0 | 5 | # |
michael@0 | 6 | # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se> |
michael@0 | 7 | # |
michael@0 | 8 | # Additions and modifications written by Benjamin Smedberg |
michael@0 | 9 | # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation |
michael@0 | 10 | # <http://www.mozilla.org/> |
michael@0 | 11 | # |
michael@0 | 12 | # More Modifications |
michael@0 | 13 | # Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com> |
michael@0 | 14 | # Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com> |
michael@0 | 15 | # |
michael@0 | 16 | # By obtaining, using, and/or copying this software and/or its |
michael@0 | 17 | # associated documentation, you agree that you have read, understood, |
michael@0 | 18 | # and will comply with the following terms and conditions: |
michael@0 | 19 | # |
michael@0 | 20 | # Permission to use, copy, modify, and distribute this software and |
michael@0 | 21 | # its associated documentation for any purpose and without fee is |
michael@0 | 22 | # hereby granted, provided that the above copyright notice appears in |
michael@0 | 23 | # all copies, and that both that copyright notice and this permission |
michael@0 | 24 | # notice appear in supporting documentation, and that the name of the |
michael@0 | 25 | # author not be used in advertising or publicity pertaining to |
michael@0 | 26 | # distribution of the software without specific, written prior |
michael@0 | 27 | # permission. |
michael@0 | 28 | # |
michael@0 | 29 | # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
michael@0 | 30 | # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. |
michael@0 | 31 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
michael@0 | 32 | # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
michael@0 | 33 | # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, |
michael@0 | 34 | # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION |
michael@0 | 35 | # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
michael@0 | 36 | |
michael@0 | 37 | from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE |
michael@0 | 38 | from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD, \ |
michael@0 | 39 | c_buffer, c_ulong, byref |
michael@0 | 40 | from qijo import QueryInformationJobObject |
michael@0 | 41 | |
michael@0 | 42 | LPVOID = c_void_p |
michael@0 | 43 | LPBYTE = POINTER(BYTE) |
michael@0 | 44 | LPDWORD = POINTER(DWORD) |
michael@0 | 45 | LPBOOL = POINTER(BOOL) |
michael@0 | 46 | |
michael@0 | 47 | def ErrCheckBool(result, func, args): |
michael@0 | 48 | """errcheck function for Windows functions that return a BOOL True |
michael@0 | 49 | on success""" |
michael@0 | 50 | if not result: |
michael@0 | 51 | raise WinError() |
michael@0 | 52 | return args |
michael@0 | 53 | |
michael@0 | 54 | |
michael@0 | 55 | # AutoHANDLE |
michael@0 | 56 | |
michael@0 | 57 | class AutoHANDLE(HANDLE): |
michael@0 | 58 | """Subclass of HANDLE which will call CloseHandle() on deletion.""" |
michael@0 | 59 | |
michael@0 | 60 | CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE) |
michael@0 | 61 | CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32)) |
michael@0 | 62 | CloseHandle.errcheck = ErrCheckBool |
michael@0 | 63 | |
michael@0 | 64 | def Close(self): |
michael@0 | 65 | if self.value and self.value != HANDLE(-1).value: |
michael@0 | 66 | self.CloseHandle(self) |
michael@0 | 67 | self.value = 0 |
michael@0 | 68 | |
michael@0 | 69 | def __del__(self): |
michael@0 | 70 | self.Close() |
michael@0 | 71 | |
michael@0 | 72 | def __int__(self): |
michael@0 | 73 | return self.value |
michael@0 | 74 | |
michael@0 | 75 | def ErrCheckHandle(result, func, args): |
michael@0 | 76 | """errcheck function for Windows functions that return a HANDLE.""" |
michael@0 | 77 | if not result: |
michael@0 | 78 | raise WinError() |
michael@0 | 79 | return AutoHANDLE(result) |
michael@0 | 80 | |
michael@0 | 81 | # PROCESS_INFORMATION structure |
michael@0 | 82 | |
michael@0 | 83 | class PROCESS_INFORMATION(Structure): |
michael@0 | 84 | _fields_ = [("hProcess", HANDLE), |
michael@0 | 85 | ("hThread", HANDLE), |
michael@0 | 86 | ("dwProcessID", DWORD), |
michael@0 | 87 | ("dwThreadID", DWORD)] |
michael@0 | 88 | |
michael@0 | 89 | def __init__(self): |
michael@0 | 90 | Structure.__init__(self) |
michael@0 | 91 | |
michael@0 | 92 | self.cb = sizeof(self) |
michael@0 | 93 | |
michael@0 | 94 | LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION) |
michael@0 | 95 | |
michael@0 | 96 | # STARTUPINFO structure |
michael@0 | 97 | |
michael@0 | 98 | class STARTUPINFO(Structure): |
michael@0 | 99 | _fields_ = [("cb", DWORD), |
michael@0 | 100 | ("lpReserved", LPWSTR), |
michael@0 | 101 | ("lpDesktop", LPWSTR), |
michael@0 | 102 | ("lpTitle", LPWSTR), |
michael@0 | 103 | ("dwX", DWORD), |
michael@0 | 104 | ("dwY", DWORD), |
michael@0 | 105 | ("dwXSize", DWORD), |
michael@0 | 106 | ("dwYSize", DWORD), |
michael@0 | 107 | ("dwXCountChars", DWORD), |
michael@0 | 108 | ("dwYCountChars", DWORD), |
michael@0 | 109 | ("dwFillAttribute", DWORD), |
michael@0 | 110 | ("dwFlags", DWORD), |
michael@0 | 111 | ("wShowWindow", WORD), |
michael@0 | 112 | ("cbReserved2", WORD), |
michael@0 | 113 | ("lpReserved2", LPBYTE), |
michael@0 | 114 | ("hStdInput", HANDLE), |
michael@0 | 115 | ("hStdOutput", HANDLE), |
michael@0 | 116 | ("hStdError", HANDLE) |
michael@0 | 117 | ] |
michael@0 | 118 | LPSTARTUPINFO = POINTER(STARTUPINFO) |
michael@0 | 119 | |
michael@0 | 120 | SW_HIDE = 0 |
michael@0 | 121 | |
michael@0 | 122 | STARTF_USESHOWWINDOW = 0x01 |
michael@0 | 123 | STARTF_USESIZE = 0x02 |
michael@0 | 124 | STARTF_USEPOSITION = 0x04 |
michael@0 | 125 | STARTF_USECOUNTCHARS = 0x08 |
michael@0 | 126 | STARTF_USEFILLATTRIBUTE = 0x10 |
michael@0 | 127 | STARTF_RUNFULLSCREEN = 0x20 |
michael@0 | 128 | STARTF_FORCEONFEEDBACK = 0x40 |
michael@0 | 129 | STARTF_FORCEOFFFEEDBACK = 0x80 |
michael@0 | 130 | STARTF_USESTDHANDLES = 0x100 |
michael@0 | 131 | |
michael@0 | 132 | # EnvironmentBlock |
michael@0 | 133 | |
michael@0 | 134 | class EnvironmentBlock: |
michael@0 | 135 | """An object which can be passed as the lpEnv parameter of CreateProcess. |
michael@0 | 136 | It is initialized with a dictionary.""" |
michael@0 | 137 | |
michael@0 | 138 | def __init__(self, dict): |
michael@0 | 139 | if not dict: |
michael@0 | 140 | self._as_parameter_ = None |
michael@0 | 141 | else: |
michael@0 | 142 | values = ["%s=%s" % (key, value) |
michael@0 | 143 | for (key, value) in dict.iteritems()] |
michael@0 | 144 | values.append("") |
michael@0 | 145 | self._as_parameter_ = LPCWSTR("\0".join(values)) |
michael@0 | 146 | |
michael@0 | 147 | # CreateProcess() |
michael@0 | 148 | |
michael@0 | 149 | CreateProcessProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 150 | LPCWSTR, # lpApplicationName |
michael@0 | 151 | LPWSTR, # lpCommandLine |
michael@0 | 152 | LPVOID, # lpProcessAttributes |
michael@0 | 153 | LPVOID, # lpThreadAttributes |
michael@0 | 154 | BOOL, # bInheritHandles |
michael@0 | 155 | DWORD, # dwCreationFlags |
michael@0 | 156 | LPVOID, # lpEnvironment |
michael@0 | 157 | LPCWSTR, # lpCurrentDirectory |
michael@0 | 158 | LPSTARTUPINFO, # lpStartupInfo |
michael@0 | 159 | LPPROCESS_INFORMATION # lpProcessInformation |
michael@0 | 160 | ) |
michael@0 | 161 | |
michael@0 | 162 | CreateProcessFlags = ((1, "lpApplicationName", None), |
michael@0 | 163 | (1, "lpCommandLine"), |
michael@0 | 164 | (1, "lpProcessAttributes", None), |
michael@0 | 165 | (1, "lpThreadAttributes", None), |
michael@0 | 166 | (1, "bInheritHandles", True), |
michael@0 | 167 | (1, "dwCreationFlags", 0), |
michael@0 | 168 | (1, "lpEnvironment", None), |
michael@0 | 169 | (1, "lpCurrentDirectory", None), |
michael@0 | 170 | (1, "lpStartupInfo"), |
michael@0 | 171 | (2, "lpProcessInformation")) |
michael@0 | 172 | |
michael@0 | 173 | def ErrCheckCreateProcess(result, func, args): |
michael@0 | 174 | ErrCheckBool(result, func, args) |
michael@0 | 175 | # return a tuple (hProcess, hThread, dwProcessID, dwThreadID) |
michael@0 | 176 | pi = args[9] |
michael@0 | 177 | return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID |
michael@0 | 178 | |
michael@0 | 179 | CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32), |
michael@0 | 180 | CreateProcessFlags) |
michael@0 | 181 | CreateProcess.errcheck = ErrCheckCreateProcess |
michael@0 | 182 | |
michael@0 | 183 | # flags for CreateProcess |
michael@0 | 184 | CREATE_BREAKAWAY_FROM_JOB = 0x01000000 |
michael@0 | 185 | CREATE_DEFAULT_ERROR_MODE = 0x04000000 |
michael@0 | 186 | CREATE_NEW_CONSOLE = 0x00000010 |
michael@0 | 187 | CREATE_NEW_PROCESS_GROUP = 0x00000200 |
michael@0 | 188 | CREATE_NO_WINDOW = 0x08000000 |
michael@0 | 189 | CREATE_SUSPENDED = 0x00000004 |
michael@0 | 190 | CREATE_UNICODE_ENVIRONMENT = 0x00000400 |
michael@0 | 191 | |
michael@0 | 192 | # flags for job limit information |
michael@0 | 193 | # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx |
michael@0 | 194 | JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 |
michael@0 | 195 | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 |
michael@0 | 196 | |
michael@0 | 197 | # XXX these flags should be documented |
michael@0 | 198 | DEBUG_ONLY_THIS_PROCESS = 0x00000002 |
michael@0 | 199 | DEBUG_PROCESS = 0x00000001 |
michael@0 | 200 | DETACHED_PROCESS = 0x00000008 |
michael@0 | 201 | |
michael@0 | 202 | # CreateJobObject() |
michael@0 | 203 | |
michael@0 | 204 | CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type |
michael@0 | 205 | LPVOID, # lpJobAttributes |
michael@0 | 206 | LPCWSTR # lpName |
michael@0 | 207 | ) |
michael@0 | 208 | |
michael@0 | 209 | CreateJobObjectFlags = ((1, "lpJobAttributes", None), |
michael@0 | 210 | (1, "lpName", None)) |
michael@0 | 211 | |
michael@0 | 212 | CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32), |
michael@0 | 213 | CreateJobObjectFlags) |
michael@0 | 214 | CreateJobObject.errcheck = ErrCheckHandle |
michael@0 | 215 | |
michael@0 | 216 | # AssignProcessToJobObject() |
michael@0 | 217 | |
michael@0 | 218 | AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 219 | HANDLE, # hJob |
michael@0 | 220 | HANDLE # hProcess |
michael@0 | 221 | ) |
michael@0 | 222 | AssignProcessToJobObjectFlags = ((1, "hJob"), |
michael@0 | 223 | (1, "hProcess")) |
michael@0 | 224 | AssignProcessToJobObject = AssignProcessToJobObjectProto( |
michael@0 | 225 | ("AssignProcessToJobObject", windll.kernel32), |
michael@0 | 226 | AssignProcessToJobObjectFlags) |
michael@0 | 227 | AssignProcessToJobObject.errcheck = ErrCheckBool |
michael@0 | 228 | |
michael@0 | 229 | # GetCurrentProcess() |
michael@0 | 230 | # because os.getPid() is way too easy |
michael@0 | 231 | GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type |
michael@0 | 232 | ) |
michael@0 | 233 | GetCurrentProcessFlags = () |
michael@0 | 234 | GetCurrentProcess = GetCurrentProcessProto( |
michael@0 | 235 | ("GetCurrentProcess", windll.kernel32), |
michael@0 | 236 | GetCurrentProcessFlags) |
michael@0 | 237 | GetCurrentProcess.errcheck = ErrCheckHandle |
michael@0 | 238 | |
michael@0 | 239 | # IsProcessInJob() |
michael@0 | 240 | try: |
michael@0 | 241 | IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 242 | HANDLE, # Process Handle |
michael@0 | 243 | HANDLE, # Job Handle |
michael@0 | 244 | LPBOOL # Result |
michael@0 | 245 | ) |
michael@0 | 246 | IsProcessInJobFlags = ((1, "ProcessHandle"), |
michael@0 | 247 | (1, "JobHandle", HANDLE(0)), |
michael@0 | 248 | (2, "Result")) |
michael@0 | 249 | IsProcessInJob = IsProcessInJobProto( |
michael@0 | 250 | ("IsProcessInJob", windll.kernel32), |
michael@0 | 251 | IsProcessInJobFlags) |
michael@0 | 252 | IsProcessInJob.errcheck = ErrCheckBool |
michael@0 | 253 | except AttributeError: |
michael@0 | 254 | # windows 2k doesn't have this API |
michael@0 | 255 | def IsProcessInJob(process): |
michael@0 | 256 | return False |
michael@0 | 257 | |
michael@0 | 258 | |
michael@0 | 259 | # ResumeThread() |
michael@0 | 260 | |
michael@0 | 261 | def ErrCheckResumeThread(result, func, args): |
michael@0 | 262 | if result == -1: |
michael@0 | 263 | raise WinError() |
michael@0 | 264 | |
michael@0 | 265 | return args |
michael@0 | 266 | |
michael@0 | 267 | ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type |
michael@0 | 268 | HANDLE # hThread |
michael@0 | 269 | ) |
michael@0 | 270 | ResumeThreadFlags = ((1, "hThread"),) |
michael@0 | 271 | ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32), |
michael@0 | 272 | ResumeThreadFlags) |
michael@0 | 273 | ResumeThread.errcheck = ErrCheckResumeThread |
michael@0 | 274 | |
michael@0 | 275 | # TerminateProcess() |
michael@0 | 276 | |
michael@0 | 277 | TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 278 | HANDLE, # hProcess |
michael@0 | 279 | UINT # uExitCode |
michael@0 | 280 | ) |
michael@0 | 281 | TerminateProcessFlags = ((1, "hProcess"), |
michael@0 | 282 | (1, "uExitCode", 127)) |
michael@0 | 283 | TerminateProcess = TerminateProcessProto( |
michael@0 | 284 | ("TerminateProcess", windll.kernel32), |
michael@0 | 285 | TerminateProcessFlags) |
michael@0 | 286 | TerminateProcess.errcheck = ErrCheckBool |
michael@0 | 287 | |
michael@0 | 288 | # TerminateJobObject() |
michael@0 | 289 | |
michael@0 | 290 | TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 291 | HANDLE, # hJob |
michael@0 | 292 | UINT # uExitCode |
michael@0 | 293 | ) |
michael@0 | 294 | TerminateJobObjectFlags = ((1, "hJob"), |
michael@0 | 295 | (1, "uExitCode", 127)) |
michael@0 | 296 | TerminateJobObject = TerminateJobObjectProto( |
michael@0 | 297 | ("TerminateJobObject", windll.kernel32), |
michael@0 | 298 | TerminateJobObjectFlags) |
michael@0 | 299 | TerminateJobObject.errcheck = ErrCheckBool |
michael@0 | 300 | |
michael@0 | 301 | # WaitForSingleObject() |
michael@0 | 302 | |
michael@0 | 303 | WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type |
michael@0 | 304 | HANDLE, # hHandle |
michael@0 | 305 | DWORD, # dwMilliseconds |
michael@0 | 306 | ) |
michael@0 | 307 | WaitForSingleObjectFlags = ((1, "hHandle"), |
michael@0 | 308 | (1, "dwMilliseconds", -1)) |
michael@0 | 309 | WaitForSingleObject = WaitForSingleObjectProto( |
michael@0 | 310 | ("WaitForSingleObject", windll.kernel32), |
michael@0 | 311 | WaitForSingleObjectFlags) |
michael@0 | 312 | |
michael@0 | 313 | INFINITE = -1 |
michael@0 | 314 | WAIT_TIMEOUT = 0x0102 |
michael@0 | 315 | WAIT_OBJECT_0 = 0x0 |
michael@0 | 316 | WAIT_ABANDONED = 0x0080 |
michael@0 | 317 | WAIT_FAILED = 0xFFFFFFFF |
michael@0 | 318 | |
michael@0 | 319 | # GetExitCodeProcess() |
michael@0 | 320 | |
michael@0 | 321 | GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 322 | HANDLE, # hProcess |
michael@0 | 323 | LPDWORD, # lpExitCode |
michael@0 | 324 | ) |
michael@0 | 325 | GetExitCodeProcessFlags = ((1, "hProcess"), |
michael@0 | 326 | (2, "lpExitCode")) |
michael@0 | 327 | GetExitCodeProcess = GetExitCodeProcessProto( |
michael@0 | 328 | ("GetExitCodeProcess", windll.kernel32), |
michael@0 | 329 | GetExitCodeProcessFlags) |
michael@0 | 330 | GetExitCodeProcess.errcheck = ErrCheckBool |
michael@0 | 331 | |
michael@0 | 332 | def CanCreateJobObject(): |
michael@0 | 333 | # Running firefox in a job (from cfx) hangs on sites using flash plugin |
michael@0 | 334 | # so job creation is turned off for now. (see Bug 768651). |
michael@0 | 335 | return False |
michael@0 | 336 | |
michael@0 | 337 | ### testing functions |
michael@0 | 338 | |
michael@0 | 339 | def parent(): |
michael@0 | 340 | print 'Starting parent' |
michael@0 | 341 | currentProc = GetCurrentProcess() |
michael@0 | 342 | if IsProcessInJob(currentProc): |
michael@0 | 343 | print >> sys.stderr, "You should not be in a job object to test" |
michael@0 | 344 | sys.exit(1) |
michael@0 | 345 | assert CanCreateJobObject() |
michael@0 | 346 | print 'File: %s' % __file__ |
michael@0 | 347 | command = [sys.executable, __file__, '-child'] |
michael@0 | 348 | print 'Running command: %s' % command |
michael@0 | 349 | process = Popen(command) |
michael@0 | 350 | process.kill() |
michael@0 | 351 | code = process.returncode |
michael@0 | 352 | print 'Child code: %s' % code |
michael@0 | 353 | assert code == 127 |
michael@0 | 354 | |
michael@0 | 355 | def child(): |
michael@0 | 356 | print 'Starting child' |
michael@0 | 357 | currentProc = GetCurrentProcess() |
michael@0 | 358 | injob = IsProcessInJob(currentProc) |
michael@0 | 359 | print "Is in a job?: %s" % injob |
michael@0 | 360 | can_create = CanCreateJobObject() |
michael@0 | 361 | print 'Can create job?: %s' % can_create |
michael@0 | 362 | process = Popen('c:\\windows\\notepad.exe') |
michael@0 | 363 | assert process._job |
michael@0 | 364 | jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInformation') |
michael@0 | 365 | print 'Job info: %s' % jobinfo |
michael@0 | 366 | limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] |
michael@0 | 367 | print 'LimitFlags: %s' % limitflags |
michael@0 | 368 | process.kill() |
michael@0 | 369 | |
michael@0 | 370 | if __name__ == '__main__': |
michael@0 | 371 | import sys |
michael@0 | 372 | from killableprocess import Popen |
michael@0 | 373 | nargs = len(sys.argv[1:]) |
michael@0 | 374 | if nargs: |
michael@0 | 375 | if nargs != 1 or sys.argv[1] != '-child': |
michael@0 | 376 | raise AssertionError('Wrong flags; run like `python /path/to/winprocess.py`') |
michael@0 | 377 | child() |
michael@0 | 378 | else: |
michael@0 | 379 | parent() |