摘要:在調試程序的時候,一般我們只能通過以下幾種方式進行調試程序中已經有的日志在代碼中插入但是以上的方法也有不方便的地方,比如對于已經在運行中的程序,就不可能停止程序后加入調試代碼和增加新的日志從的項目得到靈感,嘗試對正在運行的進程插入代碼,在程
在調試 Python 程序的時候,一般我們只能通過以下幾種方式進行調試:
程序中已經有的日志
在代碼中插入 import pdb; pdb.set_trace()
但是以上的方法也有不方便的地方, 比如對于已經在運行中的程序, 就不可能停止程序后加入 調試代碼和增加新的日志.
從 JAVA 的 BTrace(https://kenai.com/projects/btrace) 項目得到靈感,嘗試對正在運行的 Python 進程插入代碼,在程序運行到指定的函數后,自動連接遠程主機進行調試
首先介紹三個開源的項目, 本實驗需要用到這三個項目
Pyasite https://github.com/lmacken/pyrasite Tools for injecting code into running Python processes
Byteplay https://github.com/serprex/byteplay 一個字節碼維護項目,類似 java的asm/cglib
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, "192.168.1.1"), (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)環境
ubuntu 14.04 64bit LTS
Python 2.7.6
步驟在機器上安裝上面需要用到的三個項目
python server.py
在 192.168.1.1 執行 nc -l 4444
pyrasite $(ps aux |grep server.py |grep -v grep|awk "{print $2}") testinject.py
執行 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,他提供了 call, line, return, exception事件,在合適的時機調用用戶提供的函數, 具體可以參考 https://docs.python.org/2/library/sys.html#sys.settrace 的解釋
理論上可以插入任意字節碼到程序中的任意位置, 實現對現有進程中代碼的任意修改.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37537.html
摘要:進程可創建多個線程來執行同一程序的不同部分。就緒等待線程調度。運行線程正常運行阻塞暫停運行,解除阻塞后進入狀態重新等待調度。消亡線程方法執行完畢返回或者異常終止。多線程多的情況下,依次執行各線程的方法,前頭一個結束了才能執行后面一個。 淺談Python多線程 作者簡介: 姓名:黃志成(小黃)博客: 博客 線程 一.什么是線程? 操作系統原理相關的書,基本都會提到一句很經典的話: 進程...
摘要:其次,解釋器的主循環,一個名為的函數,讀取字節碼并逐個執行其中的指令。所有線程都運行相同的代碼,并以相同的方式定期從它們獲取鎖定。無論如何,其他線程無法并行運行。 概述 如今我也是使用Python寫代碼好多年了,但是我卻很少關心GIL的內部機制,導致在寫Python多線程程序的時候。今天我們就來看看CPython的源代碼,探索一下GIL的源碼,了解為什么Python里要存在這個GIL,...
摘要:倘若該回答是正確的,則立即有如下推論在處理信號的過程中,字節碼具有原子性。因此,除了在兩個字節碼之間,應該還有其他時機喚起了。行的是信號處理函數的最外層包裝,由系統調用或注冊至內核,并在信號發生時被內核回調,是異常控制流的入口。 寫在前面 前幾天工作時遇到了一個匪夷所思的問題。經過幾次嘗試后問題得以解決,但問題產生的原因卻仍令人費解。查找 SO 無果,我決定翻看 Python 的源碼。...
摘要:軟件包存儲庫正成為供應鏈攻擊的熱門目標,和等流行存儲庫已經受到惡意軟件攻擊,研究人員稱。當應用程序中的第三代碼方庫不能保持在最新狀態時,對企業來說后果可能很嚴重。 .markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}...
摘要:摘要性能彪悍的引擎。深入淺出系列深入淺出第課箭頭函數中的究竟是什么鬼深入淺出第課函數是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法深入淺出第課是如何工作的最近,生態系統又多了個非常硬核的項目。 摘要: 性能彪悍的V8引擎。 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數中的this究竟是什么鬼? JavaScript深入淺出第2課:函數是一...
閱讀 2755·2019-08-30 15:53
閱讀 521·2019-08-29 17:22
閱讀 1040·2019-08-29 13:10
閱讀 2307·2019-08-26 13:45
閱讀 2751·2019-08-26 10:46
閱讀 3202·2019-08-26 10:45
閱讀 2504·2019-08-26 10:14
閱讀 467·2019-08-23 18:23