将任意Bytecode注入运行中的Python进程

在调试 Python 程序的时候,一般我们只能通过以下几种方式进行调试:

1. 程序中已经有的日志
2. 在代码中插入 import pdb; pdb.set_trace()

但是以上的方法也有不方便的地方, 比如对于已经在运行中的程序, 就不可能停止程序后加入 调试代码和增加新的日志.

从 JAVA 的 BTrace(https://kenai.com/projects/btrace) 项目得到灵感,尝试对正在运行的 Python 进程插入代码,在程序运行到指定的函数后,自动连接远程主机进行调试

首先介绍三个开源的项目, 本实验需要用到这三个项目

1. Pyasite https://github.com/lmacken/pyrasite Tools for injecting code into running Python processes
2. Byteplay https://github.com/serprex/byteplay 一个字节码维护项目,类似 java的asm/cglib
3. Rpdb-Shell https://github.com/alex8224/Rpdb-Shell

待注入的代码, 用官方的 ``tornado hello demo`` 做例子

import tornado.ioloop
import tornado.web
import os

class 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()

注入以下代码(``testinject.py``)到 **get** 中

import sys
import dis
import inspect
from 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_FAST, ‘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‘), (CALL_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":
        return

    if func_name == "get":
        import tornado.web
        args = inspect.getargvalues(frame)
        if ‘self‘ in args.locals:
            if isinstance(args.locals[‘self‘], tornado.web.RequestHandler):
                getmethod = args.locals[‘self‘].get
                code = getmethod.__func__.__code__
                getmethod.__func__.__code__ = wearedcode(code)
        return

sys.settrace(trace)

##环境

1. ubuntu 14.04 64bit LTS

2. Python 2.7.6

##步骤

1. 在机器上安装上面需要用到的三个项目

2. python server.py

4. 在 ``192.168.1.1`` 执行 ``nc -l 4444``

3. pyrasite $(ps aux |grep server.py |grep -v grep|awk ‘{print $2}‘) testinject.py

4. 执行 curl http://localhost:8000 两次, 在第二次请求时替换的 ``bytecode`` 才会生效

##结果

在执行上面的步骤后, 在执行第二次 curl http://127.0.0.1:8000 后, 应该能够看到控制台输入 injected 的字样,并且 nc -l 4444 监听的终端会出现 ``(pdb)>`` 的字样, 这样就能够对正在运行中的程序进行调试了.

##原理

``**Pyasite**`` 可以注入代码到运行中的 Python 进程,它利用了 Python 的 ``PyRun_SimpleString`` 这个API插入代码, 至于进程注入应该是使用了 ``ptrace``

``Byteplay`` 是一个可以维护 Python bytecode的工具, 这部分跟 cglib/asm类似

``**Pyasite**`` 只能把代码注入到进程中并运行,不能定位到具体的函数并注入 bytecode, 在 ``testinject.py`` 中结合 Byteplay 完成了函数定位和替换 get 函数字节码的功能.

函数的定位用到了 sys.settrace 这个API,他提供了以下事件,在合适的时机调用用户提供的函数, 具体可以参考 https://docs.python.org/2/library/sys.html#sys.settrace 的解释

理论上可以插入任意字节码到程序中的任意位置, 实现对现有进程中代码的任意修改.

时间: 2024-10-03 21:16:04

将任意Bytecode注入运行中的Python进程的相关文章

使用 gdb 调试运行中的 Python 进程

本文和大家分享的是使用 gdb 调试运行中的 Python 进程相关内容,一起来看看吧,希望对大家学习python有所帮助. 准备工作 安装 gdb 和 python2.7-dbg: $ sudo apt-get install gdb python2.7-dbg 设置 /proc/sys/kernel/yama/ptrace_scope: $ sudo su# echo 0 > /proc/sys/kernel/yama/ptrace_scope 运行 test.py: $ python te

【Python】读取命令行参数、在PyDev中设置Python运行时的参数

有时候,我们写的命令行程序需要批次执行,这里可以让Python程序读取命令行参数,再编写一个命令行批次执行脚本.bat,实际上就是一个充满命令的.改了后缀名的文本文件,在多个测试用例扔进Python程序中是非常有用的. 例如,如下图,有一个cmdArgs.py程序,当在python cmdArgs.py 后面带上-h -i,程序则输出-h与-i参数,后面所带内容: 如果输入其它参数,比如这里多出来的-u参数,程序是自动报错的. 这个cmdArgs.py的代码如下,十分简单: #-*-coding

怎么用cmd 运行python 快捷键(WIN+R)在“运行”中输入“cmd ”然后在命令提示符中输入set PATH=%PATH%;C:\Python25,接下来,再在当前的 cmd下输入python,即可运行。———没嘛用 直接文件夹shift右键

来源http://zhidao.baidu.com/question/1510889184474081700.html?qq-pf-to=pcqq.c2c 在windows cmd下运行python,需要设置环境变量,不设环境变量是不能在cmd下运行python的, 现在假设python安装在C:\Python25目录下,设置环境变量方法如下:  方法一.我的电脑->属性->高级->环境变量->系统变量 ,在系统变量里找到PATH,双击PATH,在结尾加上 ";C:\Py

c#中调用python

1. 安装IronPython,到http://ironpython.codeplex.com/下载安装包 2. 创建项目 创建一个C#的控制台应用程序. 添加引用: 浏览到IronPython的安装目录中,添加对IronPython.dll,Microsoft.Scripting.dll 两个dll的引用. 3. 添加Python文件到当前的项目中 创建一个文本文件命名为:hello.py, 编辑如下 def welcome(name):    return "hello" + na

linux中搭建python开发环境

http://blog.csdn.net/pipisorry/article/details/39854707 ubuntu 12.04中搭建python开发环境 一.使用的系统及软件 Ubuntu 12.04 Python 2.7.3 Django 1.4.2 Pycharm 2.6.3 Postgresql 9.1 Virtualenv Virtualenvwrapper Openjdk 开始之前,可以给系统做一下备份.如误安装了Postgresql,出现了大问题就不得不把系统给重装了. 1

在Julia语言中调用Python函数

在PyCall扩展包中,模仿Python的import语句,提供了一个可以导入Python模块的@pyimport宏.并且,为能在Julia中使用模块内的函数和常量做了封装,以及支持在Julia与Python间的自动类型转换. 同时,它还提供了对Python对象进行底层操作的设施.其中包括能与不透明的Python对象相对应的'PyObjec'类型,以及在Julia语言中对Python函数进行调用且做类型转换的pycall. 安装 在Julia中,只需要使用Pkg.add("PyCall"

Python 3.X 调用多线程C模块,并在C模块中回调python函数的示例

由于最近在做一个C++面向Python的API封装项目,因此需要用到C扩展Python的相关知识.在此进行简要的总结. 此篇示例分为三部分.第一部分展示了如何用C在Windows中进行多线程编程:第二部分将第一部分的示例进行扩展,展示了如何在python中调用多线程的C模块:第三部分扩展了第二部分,增加了在C模块的线程中回调python的演示. 本文所用的环境为:64位Win7 + python 3.4 x86 + vs2010 一.windows下的C语言多线程程序 windows下多线程编程

【转】windows和linux中搭建python集成开发环境IDE

http://blog.csdn.net/pipisorry/article/details/39854707 使用的系统及软件Ubuntu / windowsPython 2.7 / python 3Pycharm 2.6.3Openjdk Postgresql 9.1VirtualenvVirtualenvwrapper{开始之前,可以给系统做一下备份.如误安装了Postgresql,出现了大问题就不得不把系统给重装了} 安装python 安装python 1. Ubuntu 12.04系统

在Mac OSX系统中搭建Python集成开发环境

本篇博客分享如何在Mac OSX系统中搭建Python集成开发环境 首先到Python官网下载python,python官网链接 这里选择下载Python2.7.9版本,下载完成之后安装: 安装成功,打开终端: 下面下载python开发的ide,http://www.jetbrains.com/pycharm/ 下载专业版,有30天的免费试用,足够我们学习python了. 安装,将Pycharm拖动到mac应用程序中 创建第一个Python项目: 运行python文件