Portál AbcLinuxu, 30. dubna 2025 18:20

Léčení pythoních bugů injekcemi do procesu

17.9.2009 00:42 | Přečteno: 1262× | programování | Výběrový blog

Pokud vím, neexistuje nástroj jak se attachnout k běžícímu pythonímu procesu, který nebyl původně spuštěn v nějakém debuggeru. Vyjma low-level debugování v GDB. S trochou hackování lze přejít i na python-level debugování.

Nemožnost attachnout se k pythonímu procesu podobně jak to umí gdb s C/C++ kódem mi dost začla vadit, když jsem měl spoustu pythoních procesů a jenom občas v některém z nich nastala race condition.

Injekce - ptrace nebo gdb

Dostal jsem nápad vyzkoušet injectnout pythoní kód, který by se připojil k pythonímu debuggeru, do běžícího pythoního procesu. Možnosti jsou asi dvě, gdb nebo ptrace. Volba padla na gdb, pro vyzkoušení je to jednodušší než přímo přes ptrace (navíc přenositelnější a není potřeba přepisovat kód v paměti).

Nejdůležitejší věc byla zjistit, kam se může kód injectnout. Když je proces uspán v systémovém volání (třeba přes time.sleep), tak to moc dobře nejde. Něco jsem zjistil experimentováním s gdb nad pythonem a něco z gdbinit, který používají pythoní vývojáři.

Magic revealed

Po attachnutí se s gdb na pythoní proces se musí breaknout na místě v pythoním stack frame (funkce PyEval_EvalFrameEx), kde se bude provádět další instrukce virtuálního stroje. V pythonu 2.6.2 se switch na opcode nachází v Python/ceval.c:952.

V promptu gdb zadám dva příkazy (viz níže definice těch příkazů):

til Python/ceval.c:952
callkomodo

Vuala! V Komodo IDE se mi objeví connection (stačí jen dát jednou "step over") a už vidím a debuguju kód, který je interpretován attachnutým pythoním procesem. S jiným debuggerem by to nespíš šlo taky, jenže myslím většina pythoních debuggerů dělá connection obráceně (debugovaný program je server, debugger je klient), asi by se muselo hackovat jinak.

GDB má machine interface, čímž lze tenhle postup automatizovat.

Použité předdefinované příkazy v ~/.gdbinit (metoda brk se připojuje na debugger):

define callkomodo
    call PyRun_SimpleString("import sys; sys.path.append('/opt/Komodo'); from dbgp.client import brk; brk('172.16.1.105', 9000);")
    continue
end

define til
    tbreak $arg0
    continue
end

Drobný problém s ladícími symboly

U sebe jsem měl přeložen python s ladícími symboly, breaknutí na konkrétním řádku není problém. Na ostrém serveru je nutné disassemblovat distribuční python a zjistit správnou adresu pro breakpoint (nebo přeložit ručně znovu). V mém pythonu ten switch(opcode) vypadá takhle:

0x00000000004a0d83 (PyEval_EvalFrameEx+1367):	cmpl   $0x8f,-0x1c8(%rbp)
0x00000000004a0d8d (PyEval_EvalFrameEx+1377):	ja     0x4a7026 (PyEval_EvalFrameEx+26618)
0x00000000004a0d93 (PyEval_EvalFrameEx+1383):	mov    -0x1c8(%rbp),%eax
0x00000000004a0d99 (PyEval_EvalFrameEx+1389):	mov    0x546270(,%rax,8),%rax
0x00000000004a0da1 (PyEval_EvalFrameEx+1397):	jmpq   *%rax
       

Hodnocení: 100 %

        špatnédobré        

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

Komentáře

Nástroje: Začni sledovat (1) ?Zašle upozornění na váš email při vložení nového komentáře. , Tisk

Vložit další komentář

davida avatar 17.9.2009 08:43 davida | skóre: 13 | blog: Blog
Rozbalit Rozbalit vše Re: Léčení pythoních bugů injekcemi do procesu
Odpovědět | Sbalit | Link | Blokovat | Admin

pekne, precteni takoveho blogu po ranu cloveku hned spravi naladu ;-)

alblaho avatar 17.9.2009 09:45 alblaho | skóre: 17 | blog: alblog
Rozbalit Rozbalit vše Re: Léčení pythoních bugů injekcemi do procesu
Odpovědět | Sbalit | Link | Blokovat | Admin
Fíha. Respekt!
limit_false avatar 17.9.2009 22:10 limit_false | skóre: 23 | blog: limit_false
Rozbalit Rozbalit vše sys.settrace(), ASLR/PIC kód bez debugovacích symbolů
Odpovědět | Sbalit | Link | Blokovat | Admin

Pro jiné pythoní debuggery by mělo stačit injectnout volání sys.settrace() se správně nastavenou trace funkcí a výsledek by měl být podobnej.

Trocha jsem to zkoušel na pythoní binárce bez ladících symbolů - musíte specifikovat adresu. Jenže třeba s ASLR + PIC kódem se adresy pořád mění. Nakonec jsem boj s gdb vyhrál :-) Lze to napsat takhle (samozřejmě je nutné znát správný offset):

til *(&PyEval_EvalFrameEx+998)

 

When people want prime order group, give them prime order group.

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.