When debugging a Python program, generally we can only debug in the following ways:
1. Logs already in the program
2. Insert the import PDB in the code; Pdb.set_trace ()
However, the above methods also have inconvenient places, such as the already running program, it is impossible to stop the program after adding debugging code and add new log.
Inspired by the JAVA BTrace(https://kenai.com/projects/btrace) project, try to insert code into the running Python process, after the program runs to the specified function, Automatic connection to remote host for debugging
First of all, we introduce three open source projects, this experiment needs to use these three projects
1. Pyasite https://github.com/lmacken/pyrasite Tools for injecting code into running Python processes
2. Byteplay https://github.com/serprex/byteplay A byte-code maintenance project, similar to Java asm/cglib
3. Rpdb-shell Https://github.com/alex8224/Rpdb-Shell
The code to be injected, using the official ' Tornado Hello Demo ' as an example
Import Tornado.ioloopimport tornado.webimport osclass MainHandler (tornado.web.RequestHandler): def get (self): self.write ("Hello, World") application = Tornado.web.Application ([ (R "/", MainHandler),]) if __name__ = = "__main__": application.listen (8888) print (Os.getpid ()) tornado.ioloop.IOLoop.instance (). Start ()
Inject the following code (' testinject.py ') into the **get**
Import sysimport disimport inspectfrom byteplay import *def wearedcode (fcode): c = Code.from_code (FCode) if c.code[1 ] = = (Load_const, ' injected '): return fcode c.code[1:1] = [(Load_const, Injected '), (store_f AST, ' name '), (load_fast, ' name '), (Print_item, none), (Print_newline, none), (Load_const,-1), (Load_const, None), (Import_name, ' rpdb '), (Store_fast, ' rpdb '), (Load_fast, ' rpdb '), (load_attr, ' Trace_to_remote '), (Load_const, ' 10.86.11.116 '), (CAL L_function, 1), (Pop_top, None)] return C.to_code () def trace (frame, event, Arg): If event! = ' call ': return CO = Frame.f_code Func_name = Co.co_name if func_name = = "Write": RE turn if Func_name = = "Get": import tornado.web args = Inspect.getargvalues (frame) if ' self ' in arg S.locals:ifIsinstance (args.locals[' self ", tornado.web.RequestHandler): GetMethod = args.locals[' self '].get Code = getmethod.__func__.__code__ getmethod.__func__.__code__ = wearedcode (code) returnsys.set Trace (Trace)
# #环境
1. Ubuntu 14.04 64bit LTS
2. Python 2.7.6
# #步骤
1. Install the three items needed on the machine
2. Python server.py
4. Execute ' nc-l 4444 ' in ' 192.168.1.1 '
3. Pyrasite $ (PS aux |grep server.py |grep-v grep|awk ' {print $} ') testinject.py
4. Perform Curl http://localhost:8000 two times and replace "bytecode" on second request to take effect
# #结果
After performing the above steps, after performing the second Curl http://127.0.0.1:8000, you should be able to see the console input injected, and nc-l 4444 listening terminal will appear "(PDB) >" Words, so that the positive Debugging in a running program.
# #原理
' **pyasite** ' can inject code into the running Python process, which takes advantage of Python's ' pyrun_simplestring ' API to insert code, as the process injection should use ' ptrace '
' Byteplay ' is a tool that can maintain Python bytecode, which is similar to Cglib/asm
' **pyasite** ' can only inject code into the process and run, cannot navigate to a specific function and inject bytecode, and in ' testinject.py ' combines byteplay to complete function positioning and replacing the function of the GET function bytecode.
function is used to locate the Sys.settrace API, he provides the following events, at the right time to invoke the user-provided functions, specifically can refer to https://docs.python.org/2/library/sys.html#sys.settrace explanation
In theory, arbitrary bytecode can be inserted anywhere in the program, enabling arbitrary modifications to the code in the existing process.
Inject any bytecode into a running Python process