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