python-ptrace-0.6.4/0000775000175000017500000000000011722433414014603 5ustar haypohaypo00000000000000python-ptrace-0.6.4/TODO0000644000175000017500000000333211527075263015301 0ustar haypohaypo00000000000000TODO ==== Main tasks ---------- * Remove ptrace.ctypes_stdint * Fix strace.py --socketcall: SyscallState.enter() calls ignore_callback before socketcall are proceed * Support other backends: - GDB MI: http://code.google.com/p/pygdb/ - ktrace: (FreeBSD and Darwin): would help Darwin support - utrace (new Linux debugger): http://sourceware.org/systemtap/wiki/utrace - vtrace? - PyDBG: works on Windows * Backtrace symbols: - GNU BFD? - elfsh? - addr2line program? - See dl_iterate_phdr() function of libdl * Support other disassemblers (than distorm), and so both Intel syntax (Intel and AT&T) - BFD - http://www.ragestorm.net/distorm/ - libasm (ERESI) - libdisasm (bastard) - http://www.woodmann.com/collaborative/tools/index.php/Category:X86_Disassembler_Libraries * Support threads: other backends (than python-ptrace) already support threads Minor tasks ----------- * setup.py: convert docstrings with 2to3 (run "2to3 -w -d ." ?) * Fix gdb.py "step" command on a jump. Example where step will never stop: :: (gdb) where ASM 0xb7e3b917: JMP 0xb7e3b8c4 (eb ab) ASM 0xb7e3b919: LEA ESI, [ESI+0x0] (8db426 00000000) * Remove gdb.py "except PtraceError, err: if err.errno == ESRCH" hack, process death detection should be done by PtraceProcess or PtraceDebugger * Use Intel hardware breakpoints: set vtrace source code * Support Darwin: - ktrace? need to recompile Darwin kernel with KTRACE option - get registers: http://unixjunkie.blogspot.com/2006/01/darwin-ptrace-and-registers.html - PT_DENY_ATTACH: http://steike.com/code/debugging-itunes-with-gdb/ - PT_DENY_ATTACH: http://landonf.bikemonkey.org/code/macosx/ptrace_deny_attach.20041010201303.11809.mojo.html python-ptrace-0.6.4/doc/0000775000175000017500000000000011722433414015350 5ustar haypohaypo00000000000000python-ptrace-0.6.4/doc/ptrace_signal.rst0000644000175000017500000000151211234417272020714 0ustar haypohaypo00000000000000++++++++++++ PtraceSignal ++++++++++++ Introduction ============ PtraceSignal tries to display useful informations when a signal is received. Depending on the signal number, it show different informations. It uses the current instruction decoded as assembler code to understand why the signal is raised. Only Intel x86 (i386, maybe x86_64) is supported now. Examples ======== Invalid read: :: Signal: SIGSEGV Invalid read from 0x00000008 - instruction: MOV EAX, [EAX+0x8] - mapping: (no memory mapping) - register eax=0x00000000 Invalid write (MOV): :: Signal: SIGSEGV Invalid write to 0x00000008 (size=4 bytes) - instruction: MOV DWORD [EAX+0x8], 0x2a - mapping: (no memory mapping) - register eax=0x00000000 abort(): :: Signal: SIGABRT Program received signal SIGABRT, Aborted. python-ptrace-0.6.4/doc/process_events.rst0000644000175000017500000000350111234417272021143 0ustar haypohaypo00000000000000Process events ============== All process events are based on ProcessEvent class. * ProcessExit: process exited with an exitcode, killed by a signal or exited abnormally * ProcessSignal: process received a signal * NewProcessEvent: new process created, eg. after a fork() syscall Attributes: * All events have a "process" attribute * ProcessExit has "exitcode" and "signum" attributes (both can be None) * ProcessSignal has "signum" and "name" attributes For NewProcessEvent, use process.parent attribute to get the parent process. Note: ProcessSignal has a display() method to display its content. Use it just after receiving the message because it reads process memory to analyze the reasons why the signal was sent. Wait for any process event ========================== The most generic function is waitProcessEvent(): it waits for any process event (exit, signal or new process): :: event = debugger.waitProcessEvent() To wait one or more signals, use waitSignals() methods. With no argument, it waits for any signal. Events different than signal are raised as Python exception. Examples: :: signal = debugger.waitSignals() signal = debugger.waitSignals(SIGTRAP) signal = debugger.waitSignals(SIGINT, SIGTERM) Note: signal is a ProcessSignal object, use signal.signum to get the signal number. Wait for a specific process events ================================== To wait any event from a process, use waitEvent() method: :: event = process.waitEvent() To wait one or more signals, use waitSignals() method. With no argument, it waits for any signal. Other process events are raised as Python exception. Examples: :: signal = process.waitSignals() signal = process.waitSignals(SIGTRAP) signal = process.waitSignals(SIGINT, SIGTERM) Note: As debugger.waitSignals(), signal is a ProcessSignal object. python-ptrace-0.6.4/ChangeLog0000644000175000017500000001122411722433122016347 0ustar haypohaypo00000000000000Changelog ========= python-ptrace 0.6.4 (2012-02-26) -------------------------------- * Convert all classes to new-style classes, patch written by teythoon * Fix compilation on Apple, patch written by Anthony Gelibert * Support GNU/kFreeBSD, patch written by Jakub Wilk * Support sockaddr_in6 (IPv6 address) python-ptrace 0.6.3 (2011-02-16) -------------------------------- * Support distrom3 * Support Python 3 * Rename strace.py option --socketcall to --socket, and fix this option for FreeBSD and Linux/64 bits * Add MANIFEST.in: include all files in source distribution (tests, cptrace module, ...) python-ptrace 0.6.2 (2009-11-09) -------------------------------- * Fix 64 bits sub registers (set mask for eax, ebx, ecx, edx) python-ptrace 0.6.1 (2009-11-07) -------------------------------- * Create follow, showfollow, resetfollow, xray commands in gdb.py. Patch written by Dimitris Glynos * Project website moved to http://bitbucket.org/haypo/python-ptrace/ * Replace types (u)intXX_t by c_(u)intXX * Create MemoryMapping.search() method and MemoryMapping now keeps a weak reference to the process python-ptrace 0.6 (2009-02-13) ------------------------------ User visible changes: * python-ptrace now depends on Python 2.5 * Invalid memory access: add fault address in the name * Update Python 3.0 conversion patch * Create -i (--show-ip) option to strace.py: show instruction pointer * Add a new example (itrace.py) written by Mark Seaborn and based on strace.py API changes: * PtraceSyscall: store the instruction pointer at syscall enter (if the option instr_pointer=True, disabled by default) * Remove PROC_DIRNAME and procFilename() from ptrace.linux_proc Bugfixes: * Fix locateProgram() for relative path * Fix interpretation of memory fault on MOSVW instruction (source is ESI and destination is EDI, and not the inverse!) python-ptrace 0.5 (2008-09-13) ------------------------------ Visible changes: * Write an example (the most simple debugger) and begin to document the code * gdb.py: create "dbginfo" command * Parse socket syscalls on FreeBSD * On invalid memory access (SIGSEGV), eval the dereference expression to get the fault address on OS without siginfo (eg. FreeBSD) * Fixes to get minimal Windows support: fix imports, fix locateProgram() Other changes: * Break the API: - Rename PtraceDebugger.traceSysgood() to PtraceDebugger.enableSysgood() - Rename PtraceDebugger.trace_sysgood to PtraceDebugger.use_sysgood - Remove PtraceProcess.readCode() * Create createChild() function which close all files except stdin, stdout and stderr * On FreeBSD, on process exit recalls waitpid(pid) to avoid zombi process python-ptrace 0.4.2 (2008-08-28) -------------------------------- * BUGFIX: Fix typo in gdb.py (commands => command_str), it wasn't possible to write more than one command... * BUGIFX: Fix typo in SignalInfo class (remove "self."). When a process received a signal SIGCHLD (because of a fork), the debugger exited because of this bug. * BUGFIX: Debugger._wait() return abnormal process exit as a normal event, the event is not raised as an exception * PtraceSignal: don't clear preformatted arguments (eg. arguments of execve) python-ptrace 0.4.1 (2008-08-23) -------------------------------- * The project has a new dedicated website: http://python-ptrace.hachoir.org/ * Create cptrace: optional Python binding of ptrace written in C (faster than ptrace, the Python binding written in Python with ctypes) * Add name attribute to SignalInfo classes * Fixes to help Python 3.0 compatibility: don't use sys.exc_clear() (was useless) in writeBacktrace() * ProcessState: create utime, stime, starttime attributes python-ptrace 0.4.0 (2008-08-19) -------------------------------- Visible changes: * Rename the project to "python-ptrace" (old name was "Ptrace) * strace.py: create --ignore-regex option * PtraceSignal: support SIGBUS, display the related registers and the instruction * Support execve() syscall tracing Developer changes: * New API is incompatible with 0.3.2 * PtraceProcess.waitProcessEvent() accepts optional blocking=False argument * PtraceProcess.getreg()/setreg() are able to read/write i386 and x86-64 "sub-registers" like al or bx * Remove iterProc() function, replaced by openProc() with explicit call to .close() to make sure that files are closed * Create searchProcessesByName() * Replace CPU_PPC constant by CPU_POWERPC and create CPU_PPC32 and CPU_PPC64 * Create MemoryMapping object, used by readMappings() and findStack() methods of PtraceProcess * Always define all PtraceProcess methods but raise an error if the function is not implemented python-ptrace-0.6.4/COPYING0000644000175000017500000004313311234417272015642 0ustar haypohaypo00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. python-ptrace-0.6.4/cptrace/0000775000175000017500000000000011722433414016224 5ustar haypohaypo00000000000000python-ptrace-0.6.4/cptrace/Makefile0000644000175000017500000000033311234417272017663 0ustar haypohaypo00000000000000CC=gcc CFLAGS=-fPIC -shared -Wall -Wextra -Wextra $(shell python-config --cflags) LIBS=$(shell python-config --libs) LIBRARY=cptrace.so $(LIBRARY): cptrace.c $(CC) -o $@ $< $(CFLAGS) $(LIBS) clean: rm -f $(LIBRARY) python-ptrace-0.6.4/cptrace/cptrace.c0000644000175000017500000000423211572532406020014 0ustar haypohaypo00000000000000#include #include #if __APPLE__ #include #endif #include #define UNUSED(arg) arg __attribute__((unused)) char python_ptrace_DOCSTR[] = "ptrace(command: int, pid: int, arg1=0, arg2=0, check_errno=False): call ptrace syscall.\r\n" "Raise a ValueError on error.\r\n" "Returns an unsigned integer.\r\n"; static bool cpython_cptrace( unsigned int request, pid_t pid, void *arg1, void *arg2, bool check_errno, unsigned long *result) { unsigned long ret; ret = ptrace(request, pid, arg1, arg2); if ((long)ret == -1) { /** * peek operations may returns -1 with errno=0: it's not an error. * For other operations, -1 is always an error */ if (!check_errno || errno) { PyErr_Format( PyExc_ValueError, "ptrace(request=%u, pid=%i, %p, %p) " "error #%i: %s", request, pid, arg1, arg2, errno, strerror(errno)); return false; } } if (result) *result = ret; return true; } static PyObject* cpython_ptrace(PyObject* UNUSED(self), PyObject *args, PyObject *keywds) { unsigned long result; unsigned int request; pid_t pid; unsigned long arg1 = 0; unsigned long arg2 = 0; bool check_errno = false; PyObject* check_errno_p = NULL; static char *kwlist[] = {"request", "pid", "arg1", "arg2", "check_errno", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "Ii|LLO", kwlist, &request, &pid, &arg1, &arg2, &check_errno_p )) { return NULL; } if (check_errno_p) { check_errno = PyObject_IsTrue(check_errno_p); } if (cpython_cptrace(request, pid, (void*)arg1, (void*)arg2, check_errno, &result)) return PyLong_FromUnsignedLong(result); else return NULL; } static PyMethodDef moduleMethods[] = { {"ptrace", (PyCFunction)cpython_ptrace, METH_VARARGS | METH_KEYWORDS, python_ptrace_DOCSTR}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initcptrace(void) { (void)Py_InitModule3("cptrace", moduleMethods, "ptrace module written in C"); } python-ptrace-0.6.4/cptrace/version.py0000644000175000017500000000015311234417272020262 0ustar haypohaypo00000000000000PACKAGE = "cptrace" VERSION = "0.6.1" WEBSITE = "http://python-ptrace.hachoir.org/" LICENSE = "GNU GPL v2" python-ptrace-0.6.4/test_doc.py0000755000175000017500000000227711527075137017001 0ustar haypohaypo00000000000000#!/usr/bin/env python from doctest import testfile, ELLIPSIS, testmod from sys import exit, path as sys_path from os.path import dirname def testDoc(filename, name=None): print("--- %s: Run tests" % filename) failure, nb_test = testfile( filename, optionflags=ELLIPSIS, name=name) if failure: exit(1) print("--- %s: End of tests" % filename) def importModule(name): mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod def testModule(name): print("--- Test module %s" % name) module = importModule(name) failure, nb_test = testmod(module) if failure: exit(1) print("--- End of test") def main(): ptrace_dir = dirname(__file__) sys_path.append(ptrace_dir) # Test documentation in doc/*.rst files #testDoc('doc/c_tools.rst') # Test documentation of some functions/classes testModule("ptrace.compatibility") testModule("ptrace.tools") testModule("ptrace.signames") testModule("ptrace.logging_tools") testModule("ptrace.debugger.parse_expr") testModule("ptrace.syscall.socketcall") if __name__ == "__main__": main() python-ptrace-0.6.4/MANIFEST.in0000644000175000017500000000063711527075137016354 0ustar haypohaypo00000000000000include AUTHORS include COPYING include ChangeLog include INSTALL include MANIFEST.in include README.cptrace include TODO include conv_python3.0.sh include cptrace/Makefile include cptrace/cptrace.c include cptrace/version.py include doc/*.rst include examples/itrace.py include examples/simple_dbg.py include pyflakes.sh include python3.0.patch include setup_cptrace.py include test_doc.py include tests/crash/*.c python-ptrace-0.6.4/setup.py0000755000175000017500000000423411722433266016326 0ustar haypohaypo00000000000000#!/usr/bin/env python # Produce to release a new version: # - ./test_doc.py # - test gdb.py # - test strace.py # - check version in ptrace/version.py # - set release date in the ChangeLog # - hg ci # - hg tag python-ptrace-x.y # - update version in ptrace/version.py # - hg ci # - hg push # - ./setup.py sdist register upload # - update the website home page (url, md5 and news) # https://bitbucket.org/haypo/python-ptrace/wiki/Home # # After the release: # - set version to n+1 (ptrace/version.py) # - add a new empty section in the changelog for version n+1 from __future__ import with_statement MODULES = ["ptrace", "ptrace.binding", "ptrace.syscall", "ptrace.debugger"] SCRIPTS = ("strace.py", "gdb.py") CLASSIFIERS = [ 'Intended Audience :: Developers', 'Development Status :: 4 - Beta', 'Environment :: Console', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Operating System :: OS Independent', 'Natural Language :: English', 'Programming Language :: Python', 'Programming Language :: Python :: 3', ] with open('README') as fp: LONG_DESCRIPTION = fp.read() with open('ChangeLog') as fp: LONG_DESCRIPTION += fp.read() from imp import load_source from os import path from sys import argv from distutils.core import setup ptrace = load_source("version", path.join("ptrace", "version.py")) PACKAGES = {} for name in MODULES: PACKAGES[name] = name.replace(".", "/") install_options = { "name": ptrace.PACKAGE, "version": ptrace.VERSION, "url": ptrace.WEBSITE, "download_url": ptrace.WEBSITE, "author": "Victor Stinner", "description": "python binding of ptrace", "long_description": LONG_DESCRIPTION, "classifiers": CLASSIFIERS, "license": ptrace.LICENSE, "packages": PACKAGES.keys(), "package_dir": PACKAGES, "scripts": SCRIPTS, } # Python 3: run 2to3 try: from distutils.command.build_py import build_py_2to3 from distutils.command.build_scripts import build_scripts_2to3 except ImportError: pass else: install_options['cmdclass'] = { 'build_py': build_py_2to3, 'build_scripts': build_scripts_2to3, } setup(**install_options) python-ptrace-0.6.4/pyflakes.sh0000755000175000017500000000014711234417272016762 0ustar haypohaypo00000000000000pyflakes $(find -name "*.py")|grep -v "redefinition of unused"|grep -v "__init__.*imported but unused" python-ptrace-0.6.4/PKG-INFO0000664000175000017500000001707511722433414015712 0ustar haypohaypo00000000000000Metadata-Version: 1.0 Name: python-ptrace Version: 0.6.4 Summary: python binding of ptrace Home-page: http://bitbucket.org/haypo/python-ptrace/wiki/Home Author: Victor Stinner Author-email: UNKNOWN License: GNU GPL v2 Download-URL: http://bitbucket.org/haypo/python-ptrace/wiki/Home Description: python-ptrace is a Python binding of ptrace library. The binding works on: * Linux version 2.6.20 on i386, x86_64, PPC (may works on Linux 2.4.x and 2.6.x) * Linux version 2.4 on PPC * FreeBSD version 7.0RC1 on i386 (may works on FreeBSD 5.x/6.x) * OpenBSD version 4.2 on i386 Features: * High level Python object API : !PtraceDebugger and !PtraceProcess * Able to control multiple processes: catch fork events on Linux * Read/write bytes to arbitrary address: take care of memory alignment and split bytes to cpu word * Execution step by step using ptrace_singlestep() or hardware interruption 3 * Can use distorm (http://www.ragestorm.net/distorm/) disassembler * Dump registers, memory mappings, stack, etc. * Syscall tracer and parser (strace command) Website: http://bitbucket.org/haypo/python-ptrace/wiki/Home Installation ============ Read INSTALL documentation file. Documentation ============= Browse doc/ and examples/ directories. Changelog ========= python-ptrace 0.6.4 (2012-02-26) -------------------------------- * Convert all classes to new-style classes, patch written by teythoon * Fix compilation on Apple, patch written by Anthony Gelibert * Support GNU/kFreeBSD, patch written by Jakub Wilk * Support sockaddr_in6 (IPv6 address) python-ptrace 0.6.3 (2011-02-16) -------------------------------- * Support distrom3 * Support Python 3 * Rename strace.py option --socketcall to --socket, and fix this option for FreeBSD and Linux/64 bits * Add MANIFEST.in: include all files in source distribution (tests, cptrace module, ...) python-ptrace 0.6.2 (2009-11-09) -------------------------------- * Fix 64 bits sub registers (set mask for eax, ebx, ecx, edx) python-ptrace 0.6.1 (2009-11-07) -------------------------------- * Create follow, showfollow, resetfollow, xray commands in gdb.py. Patch written by Dimitris Glynos * Project website moved to http://bitbucket.org/haypo/python-ptrace/ * Replace types (u)intXX_t by c_(u)intXX * Create MemoryMapping.search() method and MemoryMapping now keeps a weak reference to the process python-ptrace 0.6 (2009-02-13) ------------------------------ User visible changes: * python-ptrace now depends on Python 2.5 * Invalid memory access: add fault address in the name * Update Python 3.0 conversion patch * Create -i (--show-ip) option to strace.py: show instruction pointer * Add a new example (itrace.py) written by Mark Seaborn and based on strace.py API changes: * PtraceSyscall: store the instruction pointer at syscall enter (if the option instr_pointer=True, disabled by default) * Remove PROC_DIRNAME and procFilename() from ptrace.linux_proc Bugfixes: * Fix locateProgram() for relative path * Fix interpretation of memory fault on MOSVW instruction (source is ESI and destination is EDI, and not the inverse!) python-ptrace 0.5 (2008-09-13) ------------------------------ Visible changes: * Write an example (the most simple debugger) and begin to document the code * gdb.py: create "dbginfo" command * Parse socket syscalls on FreeBSD * On invalid memory access (SIGSEGV), eval the dereference expression to get the fault address on OS without siginfo (eg. FreeBSD) * Fixes to get minimal Windows support: fix imports, fix locateProgram() Other changes: * Break the API: - Rename PtraceDebugger.traceSysgood() to PtraceDebugger.enableSysgood() - Rename PtraceDebugger.trace_sysgood to PtraceDebugger.use_sysgood - Remove PtraceProcess.readCode() * Create createChild() function which close all files except stdin, stdout and stderr * On FreeBSD, on process exit recalls waitpid(pid) to avoid zombi process python-ptrace 0.4.2 (2008-08-28) -------------------------------- * BUGFIX: Fix typo in gdb.py (commands => command_str), it wasn't possible to write more than one command... * BUGIFX: Fix typo in SignalInfo class (remove "self."). When a process received a signal SIGCHLD (because of a fork), the debugger exited because of this bug. * BUGFIX: Debugger._wait() return abnormal process exit as a normal event, the event is not raised as an exception * PtraceSignal: don't clear preformatted arguments (eg. arguments of execve) python-ptrace 0.4.1 (2008-08-23) -------------------------------- * The project has a new dedicated website: http://python-ptrace.hachoir.org/ * Create cptrace: optional Python binding of ptrace written in C (faster than ptrace, the Python binding written in Python with ctypes) * Add name attribute to SignalInfo classes * Fixes to help Python 3.0 compatibility: don't use sys.exc_clear() (was useless) in writeBacktrace() * ProcessState: create utime, stime, starttime attributes python-ptrace 0.4.0 (2008-08-19) -------------------------------- Visible changes: * Rename the project to "python-ptrace" (old name was "Ptrace) * strace.py: create --ignore-regex option * PtraceSignal: support SIGBUS, display the related registers and the instruction * Support execve() syscall tracing Developer changes: * New API is incompatible with 0.3.2 * PtraceProcess.waitProcessEvent() accepts optional blocking=False argument * PtraceProcess.getreg()/setreg() are able to read/write i386 and x86-64 "sub-registers" like al or bx * Remove iterProc() function, replaced by openProc() with explicit call to .close() to make sure that files are closed * Create searchProcessesByName() * Replace CPU_PPC constant by CPU_POWERPC and create CPU_PPC32 and CPU_PPC64 * Create MemoryMapping object, used by readMappings() and findStack() methods of PtraceProcess * Always define all PtraceProcess methods but raise an error if the function is not implemented Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Operating System :: OS Independent Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 python-ptrace-0.6.4/README.cptrace0000644000175000017500000000056411234417272017110 0ustar haypohaypo00000000000000+++++++++++++++++++++ cptrace Python module +++++++++++++++++++++ Python binding for ptrace written in C. Example ======= Dummy example: :: >>> import cptrace >>> cptrace.ptrace(1, 1) Traceback (most recent call last): File "", line 1, in ValueError: ptrace(request=1, pid=1, 0x(nil), 0x(nil)) error #1: Operation not permitted python-ptrace-0.6.4/INSTALL0000644000175000017500000000066511234417272015643 0ustar haypohaypo00000000000000ptrace dependencies =================== * Python 2.5+: http://python.org/ * distorm disassembler (optional) http://www.ragestorm.net/distorm/ Installation ============ Type as root: :: ./setup.py install Or using sudo program: :: sudo python setup.py install cptrace ======= For faster debug, you can also install cptrace: Python binding of the ptrace() function written in C: :: ./setup_cptrace.py install python-ptrace-0.6.4/README0000644000175000017500000000171211234417316015463 0ustar haypohaypo00000000000000python-ptrace is a Python binding of ptrace library. The binding works on: * Linux version 2.6.20 on i386, x86_64, PPC (may works on Linux 2.4.x and 2.6.x) * Linux version 2.4 on PPC * FreeBSD version 7.0RC1 on i386 (may works on FreeBSD 5.x/6.x) * OpenBSD version 4.2 on i386 Features: * High level Python object API : !PtraceDebugger and !PtraceProcess * Able to control multiple processes: catch fork events on Linux * Read/write bytes to arbitrary address: take care of memory alignment and split bytes to cpu word * Execution step by step using ptrace_singlestep() or hardware interruption 3 * Can use distorm (http://www.ragestorm.net/distorm/) disassembler * Dump registers, memory mappings, stack, etc. * Syscall tracer and parser (strace command) Website: http://bitbucket.org/haypo/python-ptrace/wiki/Home Installation ============ Read INSTALL documentation file. Documentation ============= Browse doc/ and examples/ directories. python-ptrace-0.6.4/ptrace/0000775000175000017500000000000011722433414016061 5ustar haypohaypo00000000000000python-ptrace-0.6.4/ptrace/__init__.py0000644000175000017500000000012711234417272020172 0ustar haypohaypo00000000000000from ptrace.signames import SIGNAMES, signalName from ptrace.error import PtraceError python-ptrace-0.6.4/ptrace/func_call.py0000644000175000017500000000442711572532406020372 0ustar haypohaypo00000000000000from ptrace.func_arg import FunctionArgument class FunctionCallOptions(object): """ Options to format a function call and its arguments. """ def __init__(self, write_types=False, write_argname=False, replace_socketcall=True, string_max_length=300, write_address=False, max_array_count=20): self.write_address = write_address self.write_types = write_types self.write_argname = write_argname self.replace_socketcall = replace_socketcall self.string_max_length = string_max_length self.max_array_count = max_array_count self.instr_pointer = False class FunctionCall(object): """ A function call. Attributes: - name (str): function name - arguments: list of FunctionArgument objects - restype (str, optional): result type - resvalue (optional): result value - argument_class: class used to build the new arguments Methods: - format(): create a string representation of the call - addArgument(): add a new argument - clearArguments(): remove all arguments """ def __init__(self, name, options, argument_class=FunctionArgument): self.name = name self.options = options self.arguments = [] self.restype = None self.resvalue = None self.argument_class = argument_class def addArgument(self, value=None, name=None, type=None): arg = self.argument_class(self, len(self.arguments), self.options, value, type, name) self.arguments.append(arg) def clearArguments(self): self.arguments = [] def __getitem__(self, key): if isinstance(key, str): for arg in self.arguments: if arg.name == key: return arg raise KeyError("%r has no argument called %r" % (self, key)) else: # Integer key return self.arguments[key] def format(self): arguments = [ arg.format() for arg in self.arguments ] arguments = ", ".join(arguments) if self.restype and self.options.write_types: return "%s %s(%s)" % (self.restype, self.name, arguments) else: return "%s(%s)" % (self.name, arguments) def __repr__(self): return "" % self.name python-ptrace-0.6.4/ptrace/ctypes_stdint.py0000644000175000017500000000173611234417272021336 0ustar haypohaypo00000000000000""" Define standard (integers) types. Signed types: - int8_t - int16_t - int32_t - int64_t - size_t Unsigned types: - uint8_t - uint16_t - uint32_t - uint64_t """ from ctypes import sizeof, \ c_byte, c_ubyte, \ c_short, c_ushort, \ c_int, c_uint, \ c_long, c_ulong, \ c_longlong, c_ulonglong # 8-bit integers uint8_t = c_ubyte int8_t = c_byte # 16-bit integers assert sizeof(c_short) == 2 assert sizeof(c_ushort) == 2 int16_t = c_short uint16_t = c_ushort # 32-bit integers assert sizeof(c_int) == 4 assert sizeof(c_uint) == 4 int32_t = c_int uint32_t = c_uint # 64-bit integers if sizeof(c_long) == 8: int64_t = c_long else: assert sizeof(c_longlong) == 8 int64_t = c_longlong if sizeof(c_ulong) == 8: uint64_t = c_ulong else: assert sizeof(c_ulonglong) == 8 uint64_t = c_ulonglong # size_t size_t = c_long __all__ = ( "uint8_t", "int8_t", "int16_t", "uint16_t", "int32_t", "uint32_t", "int64_t", "uint64_t", "size_t") python-ptrace-0.6.4/ptrace/func_arg.py0000644000175000017500000000676611572532406020240 0ustar haypohaypo00000000000000from ptrace.error import PTRACE_ERRORS, writeError from logging import getLogger from ptrace.ctypes_tools import formatAddress class FunctionArgument(object): """ Description of a function argument. Attributes: - function: a Function objet - index (int): index of the argument (starting at zero) - options: a FunctionCallOptions objet - value (int) - type (str, optional) - text (str): string describing the argument Don't use text attribute directly, use getText() to format the argument instead. """ def __init__(self, function, index, options, value=None, type=None, name=None): self.function = function self.index = index self.options = options self.value = value self.type = type self.name = name self.text = None def getText(self): if not self.text: try: text = self.createText() if text is not None: self.text = str(text) elif self.type and self.type.endswith("*"): self.text = formatAddress(self.value) else: self.text = repr(self.value) except PTRACE_ERRORS, err: writeError(getLogger(), err, "Format argument %s of function %s() value error" % (self.name, self.function.name)) self.text = repr(self.value) return self.text def format(self): text = self.getText() options = self.options if options.write_argname and self.name: if options.write_types and self.type: return "%s %s=%s" % (self.type, self.name, text) else: return "%s=%s" % (self.name, text) elif options.write_types and self.type: return "(%s)%s" % (self.type, text) else: return text def createText(self): return repr(self.value) def formatPointer(self, value, address): if self.options.write_address: return "%s at %s" % (value, formatAddress(address)) else: return value def readStruct(self, address, struct): address = self.value struct_name = struct.__name__ data = self.function.process.readStruct(address, struct) arguments = [] for name, argtype in struct._fields_: value = getattr(data, name) try: text = self.formatStructValue(struct_name, name, value) if text is not None: text = str(text) else: text = repr(value) except PTRACE_ERRORS, err: writeError(getLogger(), err, "Format struct value error") text = repr(value) arguments.append("%s=%s" % (name, text)) data = "<%s %s>" % (struct_name, ", ".join(arguments)) return self.formatPointer(data, address) def formatStructValue(self, struct, name, value): return None def readArray(self, address, basetype, count): array = self.function.process.readArray(address, basetype, count) arguments = [] for index in xrange(count): value = array[index] value = str(value) arguments.append(value) arguments = ", ".join(arguments) return self.formatPointer("<(%s)>" % arguments, address) def __repr__(self): return "argument %s of %s()" % (self.name, self.function.name) python-ptrace-0.6.4/ptrace/linux_proc.py0000644000175000017500000001420711572532406020623 0ustar haypohaypo00000000000000""" Functions and variables to access to Linux proc directory. Constant: - PAGE_SIZE: size of a memory page """ from __future__ import with_statement from os import readlink, listdir from resource import getpagesize from ptrace.tools import timestampUNIX from datetime import timedelta PAGE_SIZE = getpagesize() class ProcError(Exception): """ Linux proc directory error. """ pass def openProc(path): """ Open a proc entry in read only mode. """ filename = "/proc/%s" % path try: return open(filename) except IOError, err: raise ProcError("Unable to open %r: %s" % (filename, err)) def readProc(path): """ Read the content of a proc entry. Eg. readProc("stat") to read /proc/stat. """ with openProc(path) as procfile: return procfile.read() def readProcessProc(pid, key): """ Read the content of a process entry in the proc directory. Eg. readProcessProc(pid, "status") to read /proc/pid/status. """ try: filename = "/proc/%s/%s" % (pid, key) with open(filename) as proc: return proc.read() except IOError, err: raise ProcError("Process %s doesn't exist: %s" % (pid, err)) class ProcessState(object): """ Processus state. Attributes: - state (str): process status ('R', 'S', 'T', ...) - program (str): program name - pid (int): process identifier - ppid (int): parent process identifier - pgrp (int): process group - session (int): session identifier - tty_nr (int): tty number - tpgid (int) - utime (int): user space time (jiffies) - stime (int): kernel space time (jiffies) - starttime (int): start time """ STATE_NAMES = { "R": "running", "S": "sleeping", "D": "disk", "Z": "zombie", "T": "traced", "W": "pagging", } def __init__(self, stat): # pid (program) ... => "pid (program", "..." part, stat = stat.rsplit(')', 1) self.pid, self.program = part.split('(', 1) self.pid = int(self.pid) # "state ..." => state, "..." stat = stat.split() self.state = stat[0] stat = [ int(item) for item in stat[1:] ] # Read next numbers self.ppid = stat[0] self.pgrp = stat[1] self.session = stat[2] self.tty_nr = stat[3] self.tpgid = stat[4] self.utime = stat[10] self.stime = stat[11] self.starttime = stat[18] def readProcessStat(pid): """ Read the process state ('stat') as a ProcessState object. """ stat = readProcessProc(pid, 'stat') return ProcessState(stat) def readProcessStatm(pid): """ Read the process memory status ('statm') as a list of integers. Values are in bytes (and not in pages). """ statm = readProcessProc(pid, 'statm') statm = [ int(item)*PAGE_SIZE for item in statm.split() ] return statm def readProcessProcList(pid, key): """ Read a process entry as a list of strings. """ data = readProcessProc(pid, key) if not data: # Empty file: empty list return [] data = data.split("\0") if not data[-1]: del data[-1] return data def readProcessLink(pid, key): """ Read a process link. """ try: filename = "/proc/%s/%s" % (pid, key) return readlink(filename) except OSError, err: raise ProcError("Unable to read proc link %r: %s" % (filename, err)) def readProcesses(): """ Read all processes identifiers. The function is a generator, use it with: :: for pid in readProcesses(): ... """ for filename in listdir('/proc'): try: yield int(filename) except ValueError: # Filename is not an integer (eg. "stat" from /proc/stat) continue def readProcessCmdline(pid, escape_stat=True): """ Read the process command line. If escape_stat is True, format program name with "[%s]" if the process has no command line, eg. "[khelper]". """ # Try /proc/42/cmdline try: cmdline = readProcessProcList(pid, 'cmdline') if cmdline: return cmdline except ProcError: pass # Try /proc/42/stat try: stat = readProcessStat(pid) program = stat.program if escape_stat: program = "[%s]" % program return [program] except ProcError: return None def searchProcessesByName(process_name): """ Find all processes matching the program name pattern. Eg. pattern "ssh" will find the program "/usr/bin/ssh". This function is a generator yielding the process identifier, use it with: :: for pid in searchProcessByName(pattern): ... """ suffix = '/'+process_name for pid in readProcesses(): cmdline = readProcessCmdline(pid) if not cmdline: continue program = cmdline[0] if program == process_name or program.endswith(suffix): yield pid def searchProcessByName(process_name): """ Function similar to searchProcessesByName() but only return the identifier of the first matching process. Raise a ProcError if there is no matching process. """ for pid in searchProcessesByName(process_name): return pid raise ProcError("Unable to find process: %r" % process_name) def getUptime(): """ Get the system uptime as a datetime.timedelta object. """ uptime = readProc('uptime') uptime = uptime.strip().split() uptime = float(uptime[0]) return timedelta(seconds=uptime) def getSystemBoot(): """ Get the system boot date as a datetime.datetime object. """ if getSystemBoot.value is None: stat_file = openProc('stat') for line in stat_file: if not line.startswith("btime "): continue seconds = int(line[6:]) btime = timestampUNIX(seconds, True) getSystemBoot.value = btime break stat_file.close() if getSystemBoot.value is None: raise ProcError("Unable to read system boot time!") return getSystemBoot.value getSystemBoot.value = None python-ptrace-0.6.4/ptrace/six.py0000644000175000017500000000051711527075137017246 0ustar haypohaypo00000000000000# Copy/paste useful parts of six, a module written by Benjamin Peterson and # distributed under the MIT license: # http://pypi.python.org/pypi/six/ import sys PY3 = sys.version_info[0] == 3 if PY3: binary_type = bytes def b(s): return s.encode("latin-1") else: binary_type = str def b(s): return s python-ptrace-0.6.4/ptrace/signames.py0000644000175000017500000000277411234417272020253 0ustar haypohaypo00000000000000""" Name of process signals. SIGNAMES contains a dictionary mapping a signal number to it's name. But you should better use signalName() instead of SIGNAMES since it returns a string even if the signal is unknown. """ PREFERRED_NAMES = ("SIGABRT", "SIGHUP", "SIGCHLD", "SIGPOLL") def getSignalNames(): """ Create signal names dictionay (eg. 9 => 'SIGKILL') using dir(signal). If multiple signal names have the same number, use the first matching name in PREFERRED_NAME to select preferred name (eg. SIGIOT=SIGABRT=17). """ import signal allnames = {} for name in dir(signal): if not name.startswith("SIG"): continue signum = getattr(signal,name) try: allnames[signum].append(name) except KeyError: allnames[signum] = [name] signames = {} for signum, names in allnames.iteritems(): if not signum: # Skip signal 0 continue name = None for preferred in PREFERRED_NAMES: if preferred in names: name = preferred break if not name: name = names[0] signames[signum] = name return signames SIGNAMES = getSignalNames() def signalName(signum): """ Get the name of a signal >>> from signal import SIGINT >>> signalName(SIGINT) 'SIGINT' >>> signalName(404) 'signal<404>' """ try: return SIGNAMES[signum] except KeyError: return "signal<%s>" % signum python-ptrace-0.6.4/ptrace/ctypes_errno.py0000644000175000017500000000365211234417272021155 0ustar haypohaypo00000000000000""" Function get_errno(): get the current errno value. Try different implementations: - ctypes_support.get_errno() function - __errno_location_sym symbol from the C library - PyErr_SetFromErrno() from the C Python API """ get_errno = None try: from ctypes_support import get_errno except ImportError: pass if not get_errno: from ctypes import POINTER, c_int def _errno_location(): """ Try to get errno integer from libc using __errno_location_sym function. This function is specific to OS with "libc.so.6" and may fails for thread-safe libc. """ from ctypes import cdll try: libc = cdll.LoadLibrary("libc.so.6") except OSError: # Unable to open libc dynamic library return None try: __errno_location = libc.__errno_location_sym except AttributeError: # libc doesn't have __errno_location return None __errno_location.restype = POINTER(c_int) return __errno_location()[0] errno = _errno_location() if errno is not None: def get_errno(): # pyflakes warn about "undefined name", # but that's wrong: errno is defined! return errno else: del errno if not get_errno: from ctypes import pythonapi, py_object # Function from pypy project: # File pypy/dist/pypy/rpython/rctypes/aerrno.py def _pythonapi_geterrno(): """ Read errno using Python C API: raise an exception with PyErr_SetFromErrno and then read error code 'errno'. This function may raise an RuntimeError. """ try: pythonapi.PyErr_SetFromErrno(py_object(OSError)) except OSError, err: return err.errno else: raise RuntimeError("get_errno() is unable to get error code") get_errno = _pythonapi_geterrno __all__ = ["get_errno"] python-ptrace-0.6.4/ptrace/disasm.py0000644000175000017500000000475711572532406017732 0ustar haypohaypo00000000000000""" Disassembler: only enabled if HAS_DISASSEMBLER is True. """ try: from ptrace.cpu_info import CPU_I386, CPU_X86_64 try: from distorm3 import Decode if CPU_X86_64: from distorm3 import Decode64Bits as DecodeBits MAX_INSTR_SIZE = 11 elif CPU_I386: from distorm3 import Decode32Bits as DecodeBits MAX_INSTR_SIZE = 8 else: raise ImportError("CPU not supported") DISTORM3 = True except ImportError, err: DISTORM3 = False from ptrace.pydistorm import Decode if CPU_X86_64: from ptrace.pydistorm import Decode64Bits as DecodeBits MAX_INSTR_SIZE = 11 elif CPU_I386: from ptrace.pydistorm import Decode32Bits as DecodeBits MAX_INSTR_SIZE = 8 else: raise ImportError("CPU not supported") from ptrace import PtraceError class Instruction(object): """ A CPU instruction. Attributes: - address (int): address of the instruction - size (int): size of the instruction in bytes - mnemonic (str): name of the instruction - operands (str): string describing the operands - hexa (str): bytes of the instruction as an hexadecimal string - text (str): string representing the whole instruction """ def __init__(self, instr): if DISTORM3: self.address, self.size, self.text, self.hexa = instr else: self.address = instr.offset self.size = instr.size self.hexa = unicode(instr.instructionHex) self.text = u"%s %s" % (instr.mnemonic, instr.operands) def __str__(self): return self.text def disassemble(code, address=0x100): """ Disassemble the specified byte string, where address is the address of the first instruction. """ for instr in Decode(address, code, DecodeBits): yield Instruction(instr) def disassembleOne(code, address=0x100): """ Disassemble the first instruction of the byte string, where address is the address of the instruction. """ for instr in disassemble(code, address): return instr raise PtraceError("Unable to disassemble %r" % code) HAS_DISASSEMBLER = True except (ImportError, OSError), err: # OSError if libdistorm64.so doesn't exist HAS_DISASSEMBLER = False python-ptrace-0.6.4/ptrace/terminal.py0000644000175000017500000000151411234417272020247 0ustar haypohaypo00000000000000""" Terminal functions. """ from termios import tcgetattr, tcsetattr, ECHO, TCSADRAIN, TIOCGWINSZ from sys import stdin, stdout from fcntl import ioctl from struct import unpack TERMIO_LFLAGS = 3 def _terminalSize(): fd = stdout.fileno() size = ioctl(fd, TIOCGWINSZ, '1234') height, width = unpack('hh', size) return (width, height) def terminalWidth(): """ Get the terminal width in characters. """ return _terminalSize()[0] def enableEchoMode(): """ Enable echo mode in the terminal. Return True if the echo mode is set correctly, or False if the mode was already set. """ fd = stdin.fileno() state = tcgetattr(fd) if state[TERMIO_LFLAGS] & ECHO: return False state[TERMIO_LFLAGS] = state[TERMIO_LFLAGS] | ECHO tcsetattr(fd, TCSADRAIN, state) return True python-ptrace-0.6.4/ptrace/ctypes_libc.py0000644000175000017500000000037411234417272020737 0ustar haypohaypo00000000000000""" Load the system C library. Variables: - LIBC_FILENAME: the C library filename - libc: the loaded library """ from ctypes import cdll from ctypes.util import find_library LIBC_FILENAME = find_library('c') libc = cdll.LoadLibrary(LIBC_FILENAME) python-ptrace-0.6.4/ptrace/process_tools.py0000644000175000017500000000734211234417272021337 0ustar haypohaypo00000000000000from ptrace.os_tools import RUNNING_LINUX, RUNNING_WINDOWS if RUNNING_LINUX: from ptrace.linux_proc import (ProcError, openProc, readProcessProcList, readProcessLink, readProcessStat) from ptrace.signames import signalName if not RUNNING_WINDOWS: from os import ( WIFSTOPPED, WSTOPSIG, WIFSIGNALED, WTERMSIG, WIFEXITED, WEXITSTATUS, WCOREDUMP) def dumpProcessInfo(log, pid, max_length=None): """ Dump all information about a process: - log: callback to write display one line - pid: process identifier - max_length (default: None): maximum number of environment variables """ if not RUNNING_LINUX: log("Process ID: %s" % pid) return try: stat = readProcessStat(pid) except ProcError: # Permission denied stat = None text = "Process ID: %s" % pid if stat: text += " (parent: %s)" % stat.ppid log(text) if stat: state = stat.state try: state = "%s (%s)" % (state, stat.STATE_NAMES[state]) except KeyError: pass log("Process state: %s" % state) try: log("Process command line: %r" % readProcessProcList(pid, 'cmdline')) except ProcError: # Permission denied pass try: env = readProcessProcList(pid, 'environ') if max_length: # Truncate environment if it's too long length = 0 removed = 0 index = 0 while index < len(env): var = env[index] if max_length < length+len(var): del env[index] removed += 1 else: length += len(var) index += 1 env = ', '.join( "%s=%r" % tuple(item.split("=", 1)) for item in env ) if removed: env += ', ... (skip %s vars)' % removed log("Process environment: %s" % env) except ProcError: # Permission denied pass try: log("Process working directory: %s" % readProcessLink(pid, 'cwd')) except ProcError: # Permission denied pass try: user = None group = None status_file = openProc("%s/status" % pid) for line in status_file: if line.startswith("Uid:"): user = [ int(id) for id in line[5:].split("\t") ] if line.startswith("Gid:"): group = [ int(id) for id in line[5:].split("\t") ] status_file.close() if user: text = "User identifier: %s" % user[0] if user[0] != user[1]: text += " (effective: %s)" % user[1] log(text) if group: text = "Group identifier: %s" % group[0] if group[0] != group[1]: text += " (effective: %s)" % group[1] log(text) except ProcError: # Permission denied pass def formatProcessStatus(status, title="Process"): """ Format a process status (integer) as a string. """ if RUNNING_WINDOWS: raise NotImplementedError() if WIFSTOPPED(status): signum = WSTOPSIG(status) text = "%s stopped by signal %s" % (title, signalName(signum)) elif WIFSIGNALED(status): signum = WTERMSIG(status) text = "%s killed by signal %s" % (title, signalName(signum)) else: if not WIFEXITED(status): raise ValueError("Invalid status: %r" % status) exitcode = WEXITSTATUS(status) if exitcode: text = "%s exited with code %s" % (title, exitcode) else: text = "%s exited normally" % title if WCOREDUMP(status): text += " (core dumped)" return text python-ptrace-0.6.4/ptrace/pydistorm.py0000644000175000017500000000657311527075137020505 0ustar haypohaypo00000000000000""" :[diStorm64 1.7.27}: Copyright RageStorm (C) 2007, Gil Dabah diStorm is licensed under the BSD license. http://ragestorm.net/distorm/ --- Python binding of diStorm64 library written by Victor Stinner """ from ctypes import cdll, c_long, c_ulong, c_int, c_uint, c_char, POINTER, Structure, addressof, byref, c_void_p, create_string_buffer, sizeof, cast from ptrace.six import binary_type # Define (u)int32_t and (u)int64_t types int32_t = c_int uint32_t = c_uint if sizeof(c_ulong) == 8: int64_t = c_long uint64_t = c_ulong else: from ctypes import c_longlong, c_ulonglong assert sizeof(c_longlong) == 8 assert sizeof(c_ulonglong) == 8 int64_t = c_longlong uint64_t = c_ulonglong SUPPORT_64BIT_OFFSET = True if SUPPORT_64BIT_OFFSET: _OffsetType = uint64_t else: _OffsetType = uint32_t LIB_FILENAME = 'libdistorm64.so' distorm = cdll.LoadLibrary(LIB_FILENAME) Decode16Bits = 0 Decode32Bits = 1 Decode64Bits = 2 DECODERS = (Decode16Bits, Decode32Bits, Decode64Bits) internal_decode = distorm.internal_decode DECRES_NONE = 0 DECRES_SUCCESS = 1 DECRES_MEMORYERR = 2 DECRES_INPUTERR = 3 MAX_INSTRUCTIONS = 100 MAX_TEXT_SIZE = 60 class _WString(Structure): _fields_ = ( ("pos", c_uint), ("p", c_char * MAX_TEXT_SIZE), ) def __str__(self): # FIXME: Use pos? return self.p class _DecodedInst(Structure): _fields_ = ( ("mnemonic", _WString), ("operands", _WString), ("instructionHex", _WString), ("size", c_uint), ("offset", _OffsetType), ) def __str__(self): return "%s %s" % (self.mnemonic, self.operands) internal_decode.argtypes = (_OffsetType, c_void_p, c_int, c_int, c_void_p, c_uint, POINTER(c_uint)) def Decode(codeOffset, code, dt=Decode32Bits): """ Errors: TypeError, IndexError, MemoryError, ValueError """ # Check arguments if not isinstance(codeOffset, (int, long)): raise TypeError("codeOffset have to be an integer") if not isinstance(code, binary_type): raise TypeError("code have to be a %s, not %s" % (binary_type.__name__, type(code).__name__)) if dt not in DECODERS: raise IndexError("Decoding-type must be either Decode16Bits, Decode32Bits or Decode64Bits.") # Allocate memory for decoder code_buffer = create_string_buffer(code) decodedInstructionsCount = c_uint() result = create_string_buffer(sizeof(_DecodedInst)*MAX_INSTRUCTIONS) # Prepare arguments codeLen = len(code) code = addressof(code_buffer) while codeLen: # Call internal decoder res = internal_decode(codeOffset, code, codeLen, dt, result, MAX_INSTRUCTIONS, byref(decodedInstructionsCount)) # Check for errors if res == DECRES_INPUTERR: raise ValueError("Invalid argument") count = decodedInstructionsCount.value if res == DECRES_MEMORYERR and not count: raise MemoryError() # No more instruction if not count: break # Yield instruction and compute decoded size size = 0 instr_array = cast(result, POINTER(_DecodedInst)) for index in xrange(count): instr = instr_array[index] size += instr.size yield instr # Update parameters to move to next instructions code += size codeOffset += size codeLen -= size python-ptrace-0.6.4/ptrace/cpu_info.py0000644000175000017500000000265711234417272020247 0ustar haypohaypo00000000000000""" Constants about the CPU: - CPU_BIGENDIAN (bool) - CPU_64BITS (bool) - CPU_WORD_SIZE (int) - CPU_MAX_UINT (int) - CPU_PPC32 (bool) - CPU_PPC64 (bool) - CPU_I386 (bool) - CPU_X86_64 (bool) - CPU_INTEL (bool) - CPU_POWERPC (bool) """ try: from os import uname HAS_UNAME = True except ImportError: HAS_UNAME = False from platform import architecture from sys import byteorder from ctypes import sizeof, c_void_p CPU_BIGENDIAN = (byteorder == 'big') CPU_64BITS = (sizeof(c_void_p) == 8) if CPU_64BITS: CPU_WORD_SIZE = 8 # bytes CPU_MAX_UINT = 0xffffffffffffffff else: CPU_WORD_SIZE = 4 # bytes CPU_MAX_UINT = 0xffffffff if HAS_UNAME: # guess machine type using uname() _machine = uname()[4] CPU_PPC32 = (_machine == 'ppc') CPU_PPC64 = (_machine == 'ppc64') CPU_I386 = (_machine in ("i386", "i686")) # compatible Intel 32 bits CPU_X86_64 = (_machine == "x86_64") # compatible Intel 64 bits del _machine else: # uname() fallback for Windows # I hope that your Windows doesn't run on PPC32/PPC64 CPU_PPC32 = False CPU_PPC64 = False CPU_I386 = False CPU_X86_64 = False bits, linkage = architecture() if bits == '32bit': CPU_I386 = True elif bits == '64bit': CPU_X86_64 = True else: raise ValueError("Unknown architecture bits: %r" % bits) CPU_INTEL = (CPU_I386 or CPU_X86_64) CPU_POWERPC = (CPU_PPC32 or CPU_PPC64) python-ptrace-0.6.4/ptrace/debugger/0000775000175000017500000000000011722433414017645 5ustar haypohaypo00000000000000python-ptrace-0.6.4/ptrace/debugger/__init__.py0000644000175000017500000000074511234417272021764 0ustar haypohaypo00000000000000from ptrace.debugger.breakpoint import Breakpoint from ptrace.debugger.process_event import (ProcessEvent, ProcessExit, NewProcessEvent, ProcessExecution) from ptrace.debugger.ptrace_signal import ProcessSignal from ptrace.debugger.process_error import ProcessError from ptrace.debugger.child import ChildError from ptrace.debugger.process import PtraceProcess from ptrace.debugger.debugger import PtraceDebugger, DebuggerError from ptrace.debugger.application import Application python-ptrace-0.6.4/ptrace/debugger/process_event.py0000644000175000017500000000351011234417272023075 0ustar haypohaypo00000000000000from ptrace.signames import signalName class ProcessEvent(Exception): """ A process event: program exit, program killed by a signal, program received a signal, etc. The attribute "process" contains the related process. """ def __init__(self, process, message): Exception.__init__(self, message) self.process = process class ProcessExit(ProcessEvent): """ Process exit event: - process kill by a signal (if signum attribute is not None) - process exited with a code (if exitcode attribute is not None) - process terminated abnormally (otherwise) """ def __init__(self, process, signum=None, exitcode=None): pid = process.pid if signum: message = "Process %s killed by signal %s" % ( pid, signalName(signum)) elif exitcode is not None: if not exitcode: message = "Process %s exited normally" % pid else: message = "Process %s exited with code %s" % (pid, exitcode) else: message = "Process %s terminated abnormally" % pid ProcessEvent.__init__(self, process, message) self.signum = signum self.exitcode = exitcode class ProcessExecution(ProcessEvent): """ Process execution: event send just after the process calls the exec() syscall if exec() tracing option is enabled. """ def __init__(self, process): ProcessEvent.__init__(self, process, "Process %s execution" % process.pid) class NewProcessEvent(ProcessEvent): """ New process: event send when a process calls the fork() syscall if fork() tracing option is enabled. The attribute process contains the new child process. """ def __init__(self, process): ProcessEvent.__init__(self, process, "New process %s" % process.pid) python-ptrace-0.6.4/ptrace/debugger/application.py0000644000175000017500000000626011234417272022526 0ustar haypohaypo00000000000000from optparse import OptionGroup from logging import (getLogger, StreamHandler, DEBUG, INFO, WARNING, ERROR) from sys import stderr, exit from ptrace import PtraceError from logging import error from ptrace.tools import locateProgram from ptrace.debugger import ProcessExit, DebuggerError from errno import EPERM from ptrace.debugger.child import createChild class Application(object): def __init__(self): pass def _setupLog(self, fd): logger = getLogger() handler = StreamHandler(fd) logger.addHandler(handler) if self.options.debug: level = DEBUG elif self.options.verbose: level = INFO elif self.options.quiet: level = ERROR else: level = WARNING logger.setLevel(level) def processOptions(self): if self.program: self.program[0] = locateProgram(self.program[0]) def createLogOptions(self, parser): log = OptionGroup(parser, "Logging") log.add_option("--quiet", "-q", help="Be quiet (set log level to ERROR)", action="store_true", default=False) log.add_option("--verbose", "-v", help="Debug mode (set log level to INFO)", action="store_true", default=False) log.add_option("--debug", help="Debug mode (set log level to DEBUG)", action="store_true", default=False) parser.add_option_group(log) def createChild(self, arguments, env=None): return createChild(arguments, self.options.no_stdout, env) def setupDebugger(self): # Set ptrace options if self.options.fork: try: self.debugger.traceFork() except DebuggerError: print >>stderr, "ERROR: --fork option is not supported by your OS, sorry!" exit(1) if self.options.trace_exec: self.debugger.traceExec() def createProcess(self): if self.options.pid: pid = self.options.pid is_attached = False error("Attach process %s" % pid) else: pid = self.createChild(self.program) is_attached = True try: return self.debugger.addProcess(pid, is_attached=is_attached) except (ProcessExit, PtraceError), err: if isinstance(err, PtraceError) \ and err.errno == EPERM: error("ERROR: You are not allowed to trace process %s (permission denied or process already traced)" % pid) else: error("ERROR: Process can no be attached! %s" % err) return None def createCommonOptions(self, parser): parser.add_option("--pid", "-p", help="Attach running process specified by its identifier", type="int", default=None) parser.add_option("--fork", "-f", help="Trace fork and child process", action="store_true", default=False) parser.add_option("--trace-exec", help="Trace execve() event", action="store_true", default=False) parser.add_option("--no-stdout", help="Use /dev/null as stdout/stderr, or close stdout and stderr if /dev/null doesn't exist", action="store_true", default=False) python-ptrace-0.6.4/ptrace/debugger/process.py0000644000175000017500000006035311572532406021706 0ustar haypohaypo00000000000000from ptrace.binding import ( HAS_PTRACE_SINGLESTEP, HAS_PTRACE_EVENTS, HAS_PTRACE_SIGINFO, HAS_PTRACE_IO, HAS_PTRACE_GETREGS, ptrace_attach, ptrace_detach, ptrace_cont, ptrace_syscall, ptrace_setregs, ptrace_peektext, ptrace_poketext, REGISTER_NAMES) if HAS_PTRACE_SINGLESTEP: from ptrace.binding import ptrace_singlestep if HAS_PTRACE_SIGINFO: from ptrace.binding import ptrace_getsiginfo if HAS_PTRACE_IO: from ctypes import create_string_buffer, addressof from ptrace.binding import ( ptrace_io, ptrace_io_desc, PIOD_READ_D, PIOD_WRITE_D) if HAS_PTRACE_EVENTS: from ptrace.binding import ( ptrace_setoptions, ptrace_geteventmsg, WPTRACEEVENT, PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK, PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC) NEW_PROCESS_EVENT = (PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK, PTRACE_EVENT_CLONE) if HAS_PTRACE_GETREGS: from ptrace.binding import ptrace_getregs else: from ptrace.binding import ptrace_peekuser, ptrace_registers_t from ptrace.os_tools import HAS_PROC, RUNNING_BSD from ptrace.tools import dumpRegs from ptrace.cpu_info import CPU_WORD_SIZE, CPU_64BITS from ptrace.ctypes_tools import bytes2word, word2bytes, bytes2type, bytes2array from signal import SIGTRAP, SIGSTOP, SIGKILL from ptrace.ctypes_tools import formatAddress, formatWordHex from ctypes import sizeof, c_char_p from logging import info, warning, error from ptrace.error import PtraceError from errno import ESRCH, EACCES from ptrace.debugger import (Breakpoint, ProcessExit, ProcessSignal, NewProcessEvent, ProcessExecution) from os import (kill, WIFSTOPPED, WSTOPSIG, WIFSIGNALED, WTERMSIG, WIFEXITED, WEXITSTATUS) from ptrace.disasm import HAS_DISASSEMBLER if HAS_DISASSEMBLER: from ptrace.disasm import disassemble, disassembleOne, MAX_INSTR_SIZE from ptrace.debugger.backtrace import getBacktrace from ptrace.debugger.process_error import ProcessError from ptrace.debugger.memory_mapping import readProcessMappings from ptrace.binding.cpu import CPU_INSTR_POINTER, CPU_STACK_POINTER, CPU_FRAME_POINTER, CPU_SUB_REGISTERS from ptrace.debugger.syscall_state import SyscallState from ptrace.six import b if HAS_PROC: from ptrace.linux_proc import readProcessStat MIN_CODE_SIZE = 32 MAX_CODE_SIZE = 1024 DEFAULT_NB_INSTR = 10 DEFAULT_CODE_SIZE = 24 class PtraceProcess(object): """ Process traced by a PtraceDebugger. Methods ======= * control execution: - singleStep(): execute one instruction - cont(): continue the execution - syscall(): break at next syscall - setInstrPointer(): change the instruction pointer - kill(): send a signal to the process - terminate(): kill the process * wait an event: - waitEvent(): wait next process event - waitSignals(): wait a signal * get status - getreg(): get a register - getInstrPointer(): get the instruction pointer - getStackPointer(): get the stack pointer - getFramePointer(): get the stack pointer - getregs(): get all registers, eg. regs=getregs(); print regs.eax - disassemble(): assembler code of the next instructions - disassembleOne(): assembler code of the next instruction - findStack(): get stack memory mapping - getsiginfo(): get signal information - getBacktrace(): get the current backtrace * set status - setreg(): set a register - setregs(): set all registers * memory access: - readWord(): read a memory word - readBytes(): read some bytes - readStruct(): read a structure - readArray(): read an array - readCString(): read a C string - readMappings(): get all memory mappings - writeWord(): write a memory word - writeBytes(): write some bytes * display status: - dumpCore(): display the next instructions - dumpStack(): display some memory words around the stack pointer - dumpMaps(): display memory mappings - dumpRegs(): display all registers * breakpoint: - createBreakpoint(): set a breakpoint - findBreakpoint(): find a breakpoint - removeBreakpoint(): remove a breakpoint * other: - setoptions(): set ptrace options See each method to get better documentation. You are responsible to manage the process state: some methods may fail or crash your processus if they are called when the process is in the wrong state. Attributes ========== * main attributes: - pid: identifier of the process - debugger: PtraceDebugger instance - breakpoints: dictionary of active breakpoints - parent: parent PtraceProcess (None if process has no parent) * state: - running: if True, the process is alive, otherwise the process doesn't exist anymore - exited: if True, the process has exited (attributed only used on BSD operation systems) - is_attached: if True, the process is attached by ptrace - was_attached: if True, the process will be detached at exit - is_stopped: if True, the process is stopped, otherwise it's running - syscall_state: control syscall tracing Sometimes, is_stopped value is wrong. You might use isTraced() to make sure that the process is stopped. """ def __init__(self, debugger, pid, is_attached, parent=None): self.debugger = debugger self.breakpoints = {} self.pid = pid self.running = True self.exited = False self.parent = parent self.was_attached = is_attached self.is_attached = False self.is_stopped = True if not is_attached: self.attach() else: self.is_attached = True if HAS_PROC: self.read_mem_file = None self.syscall_state = SyscallState(self) def isTraced(self): if not HAS_PROC: self.notImplementedError() stat = readProcessStat(self.pid) return (stat.state == 'T') def attach(self): if self.is_attached: return info("Attach process %s" % self.pid) ptrace_attach(self.pid) self.is_attached = True def dumpCode(self, start=None, stop=None, manage_bp=False, log=None): if not log: log = error try: ip = self.getInstrPointer() except PtraceError, err: if start is None: log("Unable to read instruction pointer: %s" % err) return ip = None if start is None: start = ip try: self._dumpCode(start, stop, ip, manage_bp, log) except PtraceError, err: log("Unable to dump code at %s: %s" % ( formatAddress(start), err)) def _dumpCode(self, start, stop, ip, manage_bp, log): if stop is not None: stop = max(start, stop) stop = min(stop, start + MAX_CODE_SIZE - 1) if not HAS_DISASSEMBLER: if stop is not None: size = stop - start + 1 else: size = MIN_CODE_SIZE code = self.readBytes(start, size) text = " ".join( "%02x" % ord(byte) for byte in code ) log("CODE: %s" % text) return if manage_bp: address = start for line in xrange(10): bp = False if address in self.breakpoints: bytes = self.breakpoints[address].old_bytes instr = disassembleOne(bytes, address) bp = True else: instr = self.disassembleOne(address) text = "%s| %s (%s)" % (formatAddress(instr.address), instr.text, instr.hexa) if instr.address == ip: text += " <==" if bp: text += " * BREAKPOINT *" log(text) address = address+instr.size if stop is not None and stop <= address: break else: for instr in self.disassemble(start, stop): text = "%s| %s (%s)" % (formatAddress(instr.address), instr.text, instr.hexa) if instr.address == ip: text += " <==" log(text) def disassemble(self, start=None, stop=None, nb_instr=None): if not HAS_DISASSEMBLER: self.notImplementedError() if start is None: start = self.getInstrPointer() if stop is not None: stop = max(start, stop) size = stop - start + 1 else: if nb_instr is None: nb_instr = DEFAULT_NB_INSTR size = nb_instr * MAX_INSTR_SIZE code = self.readBytes(start, size) for index, instr in enumerate(disassemble(code, start)): yield instr if nb_instr and nb_instr <= (index+1): break def disassembleOne(self, address=None): if not HAS_DISASSEMBLER: self.notImplementedError() if address is None: address = self.getInstrPointer() code = self.readBytes(address, MAX_INSTR_SIZE ) return disassembleOne(code, address) def findStack(self): for map in self.readMappings(): if map.pathname == "[stack]": return map return None def detach(self): if not self.is_attached: return self.is_attached = False if self.running: if self.was_attached: info("Detach %s" % self) ptrace_detach(self.pid) elif self.is_stopped: info("Continue process %s execution" % self.pid) self.cont() self.debugger.deleteProcess(process=self) def _notRunning(self): self.running = False if HAS_PROC and self.read_mem_file: try: self.read_mem_file.close() except IOError: pass self.detach() def kill(self, signum): kill(self.pid, signum) def terminate(self, wait_exit=True): if not self.running or not self.was_attached: return True warning("Terminate %s" % self) done = False try: if self.is_stopped: self.cont(SIGKILL) else: self.kill(SIGKILL) except PtraceError, event: if event.errno == ESRCH: done = True else: raise event if not done: if not wait_exit: return False self.waitExit() self._notRunning() return True def waitExit(self): while True: # Wait for any process signal event = self.waitEvent() event_cls = event.__class__ # Process exited: we are done if event_cls == ProcessExit: return # Event different than a signal? Raise an exception if event_cls != ProcessSignal: raise event # Send the signal to the process signum = event.signum if signum not in (SIGTRAP, SIGSTOP): self.cont(signum) else: self.cont() def processStatus(self, status): # Process exited? if WIFEXITED(status): code = WEXITSTATUS(status) event = self.processExited(code) # Process killed by a signal? elif WIFSIGNALED(status): signum = WTERMSIG(status) event = self.processKilled(signum) # Invalid process status? elif not WIFSTOPPED(status): raise ProcessError(self, "Unknown process status: %r" % status) # Ptrace event? elif HAS_PTRACE_EVENTS and WPTRACEEVENT(status): event = WPTRACEEVENT(status) event = self.ptraceEvent(event) else: signum = WSTOPSIG(status) event = self.processSignal(signum) return event def processTerminated(self): self._notRunning() return ProcessExit(self) def processExited(self, code): if RUNNING_BSD and not self.exited: # on FreeBSD, we have to waitpid() twice # to avoid zombi process!? self.exited = True self.waitExit() self._notRunning() return ProcessExit(self, exitcode=code) def processKilled(self, signum): self._notRunning() return ProcessExit(self, signum=signum) def processSignal(self, signum): self.is_stopped = True return ProcessSignal(signum, self) def ptraceEvent(self, event): if not HAS_PTRACE_EVENTS: self.notImplementedError() if event in NEW_PROCESS_EVENT: new_pid = ptrace_geteventmsg(self.pid) new_process = self.debugger.addProcess(new_pid, is_attached=True, parent=self) return NewProcessEvent(new_process) elif event == PTRACE_EVENT_EXEC: return ProcessExecution(self) else: raise ProcessError(self, "Unknown ptrace event: %r" % event) def getregs(self): if HAS_PTRACE_GETREGS: return ptrace_getregs(self.pid) else: # FIXME: Optimize getreg() when used with this function words = [] nb_words = sizeof(ptrace_registers_t) // CPU_WORD_SIZE for offset in xrange(nb_words): word = ptrace_peekuser(self.pid, offset*CPU_WORD_SIZE) bytes = word2bytes(word) words.append(bytes) bytes = ''.join(words) return bytes2type(bytes, ptrace_registers_t) def getreg(self, name): try: name, shift, mask = CPU_SUB_REGISTERS[name] except KeyError: shift = 0 mask = None if name not in REGISTER_NAMES: raise ProcessError(self, "Unknown register: %r" % name) regs = self.getregs() value = getattr(regs, name) value >>= shift if mask: value &= mask return value def setregs(self, regs): ptrace_setregs(self.pid, regs) def setreg(self, name, value): regs = self.getregs() if name in CPU_SUB_REGISTERS: full_name, shift, mask = CPU_SUB_REGISTERS[name] full_value = getattr(regs, full_name) full_value &= ~mask full_value |= ((value & mask) << shift) value = full_value name = full_name if name not in REGISTER_NAMES: raise ProcessError(self, "Unknown register: %r" % name) setattr(regs, name, value) self.setregs(regs) def singleStep(self): if not HAS_PTRACE_SINGLESTEP: self.notImplementedError() ptrace_singlestep(self.pid) def filterSignal(self, signum): if signum == SIGTRAP: # Never transfer SIGTRAP signal return 0 else: return signum def syscall(self, signum=0): signum = self.filterSignal(signum) ptrace_syscall(self.pid, signum) self.is_stopped = False def setInstrPointer(self, ip): if CPU_INSTR_POINTER: self.setreg(CPU_INSTR_POINTER, ip) else: raise ProcessError(self, "Instruction pointer is not defined") def getInstrPointer(self): if CPU_INSTR_POINTER: return self.getreg(CPU_INSTR_POINTER) else: raise ProcessError(self, "Instruction pointer is not defined") def getStackPointer(self): if CPU_STACK_POINTER: return self.getreg(CPU_STACK_POINTER) else: raise ProcessError(self, "Instruction pointer is not defined") def getFramePointer(self): if CPU_FRAME_POINTER: return self.getreg(CPU_FRAME_POINTER) else: raise ProcessError(self, "Instruction pointer is not defined") def _readBytes(self, address, size): offset = address % CPU_WORD_SIZE if offset: # Read word address -= offset word = self.readWord(address) bytes = word2bytes(word) # Read some bytes from the word subsize = min(CPU_WORD_SIZE - offset, size) data = bytes[offset:offset+subsize] # <-- FIXME: Big endian! # Move cursor size -= subsize address += CPU_WORD_SIZE else: data = b('') while size: # Read word word = self.readWord(address) bytes = word2bytes(word) # Read bytes from the word if size < CPU_WORD_SIZE: data += bytes[:size] # <-- FIXME: Big endian! break data += bytes # Move cursor size -= CPU_WORD_SIZE address += CPU_WORD_SIZE return data def readWord(self, address): """Address have to be aligned!""" word = ptrace_peektext(self.pid, address) return word if HAS_PTRACE_IO: def readBytes(self, address, size): buffer = create_string_buffer(size) io_desc = ptrace_io_desc( piod_op=PIOD_READ_D, piod_offs=address, piod_addr=addressof(buffer), piod_len=size) ptrace_io(self.pid, io_desc) return buffer.raw elif HAS_PROC: def readBytes(self, address, size): if not self.read_mem_file: filename = '/proc/%u/mem' % self.pid try: self.read_mem_file = open(filename, 'rb', 0) except IOError, err: message = "Unable to open %s: fallback to ptrace implementation" % filename if err.errno != EACCES: error(message) else: info(message) self.readBytes = self._readBytes return self.readBytes(address, size) try: mem = self.read_mem_file mem.seek(address) return mem.read(size) except (IOError, ValueError), err: raise ProcessError(self, "readBytes(%s, %s) error: %s" % ( formatAddress(address), size, err)) else: readBytes = _readBytes def getsiginfo(self): if not HAS_PTRACE_SIGINFO: self.notImplementedError() return ptrace_getsiginfo(self.pid) def writeBytes(self, address, bytes): if HAS_PTRACE_IO: size = len(bytes) bytes = create_string_buffer(bytes) io_desc = ptrace_io_desc( piod_op=PIOD_WRITE_D, piod_offs=address, piod_addr=addressof(bytes), piod_len=size) ptrace_io(self.pid, io_desc) else: offset = address % CPU_WORD_SIZE if offset: # Write partial word (end) address -= offset size = CPU_WORD_SIZE - offset word = self.readBytes(address, CPU_WORD_SIZE) if len(bytes) < size: size = len(bytes) word = word[:offset] + bytes[:size] + word[offset + size:] # <-- FIXME: Big endian! else: word = word[:offset] + bytes[:size] # <-- FIXME: Big endian! self.writeWord(address, bytes2word(word)) bytes = bytes[size:] address += CPU_WORD_SIZE # Write full words while CPU_WORD_SIZE <= len(bytes): # Read one word word = bytes[:CPU_WORD_SIZE] word = bytes2word(word) self.writeWord(address, word) # Move to next word bytes = bytes[CPU_WORD_SIZE:] address += CPU_WORD_SIZE if not bytes: return # Write partial word (begin) size = len(bytes) word = self.readBytes(address, CPU_WORD_SIZE) # FIXME: Write big endian version of the next line word = bytes + word[size:] self.writeWord(address, bytes2word(word)) def readStruct(self, address, struct): bytes = self.readBytes(address, sizeof(struct)) if not CPU_64BITS: bytes = c_char_p(bytes) return bytes2type(bytes, struct) def readArray(self, address, basetype, count): bytes = self.readBytes(address, sizeof(basetype)*count) if not CPU_64BITS: bytes = c_char_p(bytes) return bytes2array(bytes, basetype, count) def readCString(self, address, max_size, chunk_length=256): string = [] size = 0 truncated = False while True: done = False data = self.readBytes(address, chunk_length) pos = data.find(b('\0')) if pos != -1: done = True data = data[:pos] if max_size <= size+chunk_length: data = data[:(max_size-size)] string.append(data) truncated = True break string.append(data) if done: break size += chunk_length address += chunk_length return ''.join(string), truncated def dumpStack(self, log=None): if not log: log = error stack = self.findStack() if stack: log("STACK: %s" % stack) self._dumpStack(log) def _dumpStack(self, log): sp = self.getStackPointer() displayed = 0 for index in xrange(-5, 5+1): delta = index * CPU_WORD_SIZE try: value = self.readWord(sp + delta) log("STACK%+ 3i: %s" % (delta, formatWordHex(value))) displayed += 1 except PtraceError: pass if not displayed: log("ERROR: unable to read the stack (SP=%s)" % formatAddress(sp)) def readMappings(self): return readProcessMappings(self) def dumpMaps(self, log=None): if not log: log = error for map in self.readMappings(): log("MAPS: %s" % map) def writeWord(self, address, word): """ Address have to be aligned! """ ptrace_poketext(self.pid, address, word) def dumpRegs(self, log=None): if not log: log = error try: regs = self.getregs() dumpRegs(log, regs) except PtraceError, err: log("Unable to read registers: %s" % err) def cont(self, signum=0): signum = self.filterSignal(signum) ptrace_cont(self.pid, signum) self.is_stopped = False def setoptions(self, options): if not HAS_PTRACE_EVENTS: self.notImplementedError() info("Set %s options to %s" % (self, options)) ptrace_setoptions(self.pid, options) def waitEvent(self): return self.debugger.waitProcessEvent(pid=self.pid) def waitSignals(self, *signals): return self.debugger.waitSignals(*signals, **{'pid': self.pid}) def waitSyscall(self): self.debugger.waitSyscall(self) def findBreakpoint(self, address): for bp in self.breakpoints.itervalues(): if bp.address <= address < bp.address + bp.size: return bp return None def createBreakpoint(self, address, size=1): bp = self.findBreakpoint(address) if bp: raise ProcessError(self, "A breakpoint is already set: %s" % bp) bp = Breakpoint(self, address, size) self.breakpoints[address] = bp return bp def getBacktrace(self, max_args=6, max_depth=20): return getBacktrace(self, max_args=max_args, max_depth=max_depth) def removeBreakpoint(self, breakpoint): del self.breakpoints[breakpoint.address] def __del__(self): try: self.detach() except PtraceError: pass def __str__(self): return self.__repr__() def __repr__(self): return "" % self.pid def __hash__(self): return hash(self.pid) def notImplementedError(self): raise NotImplementedError() python-ptrace-0.6.4/ptrace/debugger/debugger.py0000644000175000017500000002030311572532406022003 0ustar haypohaypo00000000000000from logging import info, warning, error from ptrace import PtraceError from os import waitpid, WNOHANG from signal import SIGTRAP, SIGSTOP from errno import ECHILD from ptrace.debugger import PtraceProcess, ProcessSignal from ptrace.binding import HAS_PTRACE_EVENTS if HAS_PTRACE_EVENTS: from ptrace.binding.func import ( PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK, PTRACE_O_TRACEEXEC, PTRACE_O_TRACESYSGOOD) class DebuggerError(PtraceError): pass class PtraceDebugger(object): """ Debugger managing one or multiple processes at the same time. Methods ======= * Process list: - addProcess(): add a new process - deleteProcess(): remove a process from the debugger * Wait for an event: - waitProcessEvent(): wait for a process event - waitSignals(): wait for a signal - waitSyscall(): wait for the next syscall event * Options: - traceForks(): enable fork tracing - traceExec(): enable exec() tracing - enableSysgood(): enable sysgood option * Other: - quit(): quit the debugger, terminate all processes Operations ========== - iterarate on all processes: "for process in debugger: ..." - get a process by its identifier: "process = debugger[pid]" - get the number of processes: len(debugger) Attributes ========== - dict: processes dictionary (pid -> PtraceProcess) - list: processes list - options: ptrace options - trace_fork (bool): fork() tracing is enabled? - trace_exec (bool): exec() tracing is enabled? - use_sysgood (bool): sysgood option is enabled? """ def __init__(self): self.dict = {} # pid -> PtraceProcess object self.list = [] self.options = 0 self.trace_fork = False self.trace_exec = False self.use_sysgood = False self.enableSysgood() def addProcess(self, pid, is_attached, parent=None): """ Add a new process using its identifier. Use is_attached=False to attach an existing (running) process, and is_attached=True to trace a new (stopped) process. """ if pid in self.dict: raise KeyError("The process %s is already registered!" % pid) process = PtraceProcess(self, pid, is_attached, parent=parent) info("Attach %s to debugger" % process) self.dict[pid] = process self.list.append(process) try: process.waitSignals(SIGTRAP, SIGSTOP) except KeyboardInterrupt: error( "User interrupt! Force the process %s attach " "(don't wait for signals)." % pid) except ProcessSignal, event: event.display() except: process.is_attached = False process.detach() raise if HAS_PTRACE_EVENTS and self.options: process.setoptions(self.options) return process def quit(self): """ Quit the debugger: terminate all processes in reverse order. """ info("Quit debugger") # Terminate processes in reverse order # to kill children before parents processes = list(self.list) for process in reversed(processes): process.terminate() process.detach() def _waitpid(self, wanted_pid, blocking=True): """ Wait for a process event from a specific process (if wanted_pid is set) or any process (wanted_pid=None). The call is blocking is blocking option is True. Return the tuple (pid, status). See os.waitpid() documentation for explainations about the result. """ flags = 0 if not blocking: flags |= WNOHANG if wanted_pid: if wanted_pid not in self.dict: raise DebuggerError("Unknown PID: %r" % wanted_pid, pid=wanted_pid) pid, status = waitpid(wanted_pid, flags) else: pid, status = waitpid(-1, flags) if (blocking or pid) and wanted_pid and (pid != wanted_pid): raise DebuggerError("Unwanted PID: %r (instead of %s)" % (pid, wanted_pid), pid=pid) return pid, status def _wait(self, wanted_pid, blocking=True): """ Wait for a process event from the specified process identifier. If blocking=False, return None if there is no new event, otherwise return an objet based on ProcessEvent. """ process = None while not process: try: pid, status = self._waitpid(wanted_pid, blocking) except OSError, err: if err.errno == ECHILD: process = self.dict[wanted_pid] return process.processTerminated() else: raise err if not blocking and not pid: return None try: process = self.dict[pid] except KeyError: warning("waitpid() warning: Unknown PID %r" % pid) return process.processStatus(status) def waitProcessEvent(self, pid=None, blocking=True): """ Wait for a process event from a specific process (if pid option is set) or any process (default). If blocking=False, return None if there is no new event, otherwise return an objet based on ProcessEvent. """ return self._wait(pid, blocking) def waitSignals(self, *signals, **kw): """ Wait for any signal or some specific signals (if specified) from a specific process (if pid keyword is set) or any process (default). Return a ProcessSignal object or raise an unexpected ProcessEvent. """ pid = kw.get('pid', None) while True: event = self._wait(pid) if event.__class__ != ProcessSignal: raise event signum = event.signum if signum in signals or not signals: return event raise event def waitSyscall(self, process=None): """ Wait for the next syscall event (enter or exit) for a specific process (if specified) or any process (default). Return a ProcessSignal object or raise an unexpected ProcessEvent. """ signum = SIGTRAP if self.use_sysgood: signum |= 0x80 if process: return self.waitSignals(signum, pid=process.pid) else: return self.waitSignals(signum) def deleteProcess(self, process=None, pid=None): """ Delete a process from the process list. """ if not process: try: process = self.dict[pid] except KeyError: return try: del self.dict[process.pid] except KeyError: pass try: self.list.remove(process) except ValueError: pass def traceFork(self): """ Enable fork() tracing. Do nothing if it's not supported. """ if not HAS_PTRACE_EVENTS: raise DebuggerError("Tracing fork events is not supported on this architecture or operating system") self.options |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK self.trace_fork = True info("Debugger trace forks (options=%s)" % self.options) def traceExec(self): """ Enable exec() tracing. Do nothing if it's not supported. """ if not HAS_PTRACE_EVENTS: # no effect on OS without ptrace events return self.trace_exec = True self.options |= PTRACE_O_TRACEEXEC def enableSysgood(self): """ Enable sysgood option: ask the kernel to set bit #7 of the signal number if the signal comes from the kernel space. If the signal comes from the user space, the bit is unset. """ if not HAS_PTRACE_EVENTS: # no effect on OS without ptrace events return self.use_sysgood = True self.options |= PTRACE_O_TRACESYSGOOD def __getitem__(self, pid): return self.dict[pid] def __iter__(self): return iter(self.list) def __len__(self): return len(self.list) python-ptrace-0.6.4/ptrace/debugger/syscall_state.py0000644000175000017500000000252611572532406023100 0ustar haypohaypo00000000000000from ptrace.syscall import PtraceSyscall from signal import SIGTRAP class SyscallState(object): def __init__(self, process): self.process = process self.ignore_exec_trap = True self.ignore_callback = None self.clear() def event(self, options): if self.next_event == "exit": return self.exit() else: return self.enter(options) def enter(self, options): # syscall enter regs = self.process.getregs() self.syscall = PtraceSyscall(self.process, options, regs) self.name = self.syscall.name if (not self.ignore_callback) \ or (not self.ignore_callback(self.syscall)): self.syscall.enter(regs) else: self.syscall = None self.next_event = "exit" return self.syscall def exit(self): if self.syscall: self.syscall.exit() if self.ignore_exec_trap \ and self.name == "execve" \ and not self.process.debugger.trace_exec: # Ignore the SIGTRAP after exec() syscall exit self.process.syscall() self.process.waitSignals(SIGTRAP) syscall = self.syscall self.clear() return syscall def clear(self): self.syscall = None self.name = None self.next_event = "enter" python-ptrace-0.6.4/ptrace/debugger/signal_reason.py0000644000175000017500000001302211234417272023041 0ustar haypohaypo00000000000000from ptrace.ctypes_tools import formatAddress, formatWordHex from ptrace.error import PtraceError from ptrace.cpu_info import CPU_I386, CPU_X86_64 from ptrace.process_tools import formatProcessStatus import re # Find all Intel registers (in upper case) if CPU_I386: regex = 'E[A-Z]{2}|[CDEFGS]S|[ABCD]L' elif CPU_X86_64: regex = '[ER][A-Z]{2}|[CDEFGS]S|[ABCD]L' else: regex = None if regex: REGISTER_REGEX = re.compile(r'\b(?:%s)\b' % regex) else: REGISTER_REGEX = None def extractRegisters(process, instr): registers = {} if not process or not instr or not REGISTER_REGEX: return registers asm = instr.text asm = asm.upper() # Skip the operator ("MOV CL, [EAX]" => "CL, [EAX]") asm = asm.split(" ", 1)[1] for match in REGISTER_REGEX.finditer(asm): name = match.group(0) name = name.lower() try: value = process.getreg(name) registers[name] = value except PtraceError, err: pass return registers def findMappings(addresses, process, size): mappings = [] if addresses is None or not process: return mappings if not isinstance(addresses, (list, tuple)): addresses = (addresses,) if not size: size = 0 process_mappings = process.readMappings() if not process_mappings: return mappings for address in addresses: address_str = formatAddress(address) if 1 < size: address_str += "..%s" % formatAddress(address + size - 1) found = False for map in process_mappings: if (map.start <= address < map.end) \ or (map.start <= (address + size - 1) < map.end): found = True mappings.append("%s is part of %s" % (address_str, map)) if not found: mappings.append("%s is not mapped in memory" % address_str) return mappings class SignalInfo(Exception): def __init__(self, name, text, address=None, size=None, instr=None, process=None, registers=None): Exception.__init__(self, text) self.name = name self.text = text self.instr = instr self.registers = extractRegisters(process, instr) if registers: self.registers.update(registers) self.mappings = findMappings(address, process, size) def display(self, log): log(self.text) self.displayExtra(log) if self.instr: log("- instruction: %s" % self.instr) for mapping in self.mappings: log("- mapping: %s" % mapping) for name, value in self.registers.iteritems(): log("- register %s=%s" % (name, formatWordHex(value))) def displayExtra(self, log): pass class DivisionByZero(SignalInfo): def __init__(self, instr=None, process=None): SignalInfo.__init__(self, "div_by_zero", "Division by zero", instr=instr, process=process) class Abort(SignalInfo): def __init__(self): SignalInfo.__init__(self, "abort", "Program received signal SIGABRT, Aborted.") class StackOverflow(SignalInfo): def __init__(self, stack_ptr, stack_map, instr=None, process=None): text = "STACK OVERFLOW! Stack pointer is in %s" % stack_map SignalInfo.__init__(self, "stack_overflow", text, address=stack_ptr, registers={'': stack_ptr}, instr=instr, process=process) self.stack_ptr = stack_ptr self.stack_map = stack_map class InvalidMemoryAcces(SignalInfo): NAME = "invalid_mem_access" PREFIX = "Invalid memory access" PREFIX_ADDR = "Invalid memory access to %s" def __init__(self, address=None, size=None, instr=None, registers=None, process=None): """ address is an integer or a list of integer """ if address is not None: if isinstance(address, (list, tuple)): arguments = " or ".join( formatAddress(addr) for addr in address ) else: arguments = formatAddress(address) message = self.PREFIX_ADDR % arguments else: message = self.PREFIX if size: message += " (size=%s bytes)" % size name = self.NAME if address is not None: name += "-" + formatAddress(address).lower() SignalInfo.__init__(self, name, message, address=address, size=size, instr=instr, process=process, registers=registers) class InvalidRead(InvalidMemoryAcces): NAME = "invalid_read" PREFIX = "Invalid read" PREFIX_ADDR = "Invalid read from %s" class InvalidWrite(InvalidMemoryAcces): NAME = "invalid_write" PREFIX = "Invalid write" PREFIX_ADDR = "Invalid write to %s" class InstructionError(SignalInfo): def __init__(self, address, process=None): SignalInfo.__init__(self, "instr_error", "UNABLE TO EXECUTE CODE AT %s (SEGMENTATION FAULT)" % formatAddress(address), address=address, process=process, registers={'': address}) class ChildExit(SignalInfo): def __init__(self, pid=None, status=None, uid=None): if pid is not None and status is not None: message = formatProcessStatus(status, "Child process %s" % pid) else: message = "Child process exited" SignalInfo.__init__(self, "child_exit", message) self.pid = pid self.status = status self.uid = uid def displayExtra(self, log): if self.uid is not None: log("Signal sent by user %s" % self.uid) python-ptrace-0.6.4/ptrace/debugger/process_error.py0000644000175000017500000000031611234417272023106 0ustar haypohaypo00000000000000from ptrace.error import PtraceError class ProcessError(PtraceError): def __init__(self, process, message): PtraceError.__init__(self, message, pid=process.pid) self.process = process python-ptrace-0.6.4/ptrace/debugger/parse_expr.py0000644000175000017500000000377711234417272022405 0ustar haypohaypo00000000000000import re # Match a register name: $eax, $gp0, $orig_eax REGISTER_REGEX = re.compile(r"([a-z]+[a-z0-9_]+)") # Hexadacimel number (eg. 0xa) HEXADECIMAL_REGEX = re.compile(r"0x[0-9a-f]+") # Make sure that the expression does not contain invalid characters # Examples: # (1-2)<<5 # 340&91 EXPR_REGEX = re.compile(r"^[()<>+*/&0-9-]+$") def replaceHexadecimal(regs): """ Convert an hexadecimal number to decimal number (as string). Callback used by parseExpression(). """ text = regs.group(0) if text.startswith("0x"): text = text[2:] elif not re.search("[a-f]", text): return text value = int(text, 16) return str(value) def parseExpression(process, text): """ Parse an expression. Syntax: - "10": decimal number - "0x10": hexadecimal number - "eax": register value - "a+b", "a-b", "a*b", "a/b", "a**b", "a<>b": operators >>> from ptrace.mockup import FakeProcess >>> process = FakeProcess() >>> parseExpression(process, "1+1") 2 >>> process.setreg("eax", 3) >>> parseExpression(process, "eax*0x10") 48 """ # Remove spaces and convert to lower case text = text.strip() orig_text = text if " " in text: raise ValueError("Space are forbidden: %r" % text) text = text.lower() def readRegister(regs): name = regs.group(1) value = process.getreg(name) return str(value) # Replace hexadecimal by decimal text = HEXADECIMAL_REGEX.sub(replaceHexadecimal, text) # Replace registers by their value text = REGISTER_REGEX.sub(readRegister, text) # Reject invalid characters if not EXPR_REGEX.match(text): raise ValueError("Invalid expression: %r" % orig_text) # Use integer division (a//b) instead of float division (a/b) text = text.replace("/", "//") # Finally, evaluate the expression try: value = eval(text) except SyntaxError: raise ValueError("Invalid expression: %r" % orig_text) return value python-ptrace-0.6.4/ptrace/debugger/ptrace_signal.py0000644000175000017500000001623211234417272023036 0ustar haypohaypo00000000000000from logging import error from ptrace.disasm import HAS_DISASSEMBLER from signal import SIGFPE, SIGSEGV, SIGABRT try: from signal import SIGCHLD except ImportError: SIGCHLD = None try: from signal import SIGBUS except ImportError: SIGBUS = None from ptrace.os_tools import RUNNING_LINUX from ptrace.cpu_info import CPU_64BITS from ptrace.debugger import ProcessEvent from ptrace.error import PtraceError from ptrace import signalName from ptrace.debugger.signal_reason import ( DivisionByZero, Abort, StackOverflow, InvalidMemoryAcces, InvalidRead, InvalidWrite, InstructionError, ChildExit) from ptrace.debugger.parse_expr import parseExpression import re # Match a pointer dereference (eg. "DWORD [EDX+0x8]") DEREF_REGEX = r'(?P(BYTE|WORD|DWORD|DQWORD) )?\[(?P[^]]+)\]' NAMED_WORD_SIZE = { 'BYTE': 1, 'WORD': 2, 'DWORD': 4, 'DQWORD': 8, } # Match any Intel instruction (eg. "ADD") INSTR_REGEX = '(?:[A-Z]{3,10})' def findDerefSize(match): name = match.group("deref_size") try: return NAMED_WORD_SIZE[name] except KeyError: return None def evalFaultAddress(process, match): expr = match.group('deref') if not expr: return None try: return parseExpression(process, expr) except ValueError, err: print "err: %s" % err return None class ProcessSignal(ProcessEvent): def __init__(self, signum, process): # Initialize attributes self.name = signalName(signum) ProcessEvent.__init__(self, process, "Signal %s" % self.name) self.signum = signum self.reason = None def _analyze(self): if self.signum in (SIGSEGV, SIGBUS): self.memoryFault() elif self.signum == SIGFPE: self.mathError() elif self.signum == SIGCHLD: self.childExit() elif self.signum == SIGABRT: self.reason = Abort() return self.reason def getInstruction(self): if not HAS_DISASSEMBLER: return None try: return self.process.disassembleOne() except PtraceError: return None def memoryFaultInstr(self, instr, fault_address): asm = instr.text # Invalid write (eg. "MOV [...], value") match = re.search(r"^(?:MOV|TEST)[A-Z]* %s," % DEREF_REGEX, asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidWrite(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # Invalid read (eg. "CMP BYTE [EAX+EDX-0x1], 0x0") match = re.search(r"^%s %s," % (INSTR_REGEX, DEREF_REGEX), asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidRead(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # Invalid read (eg. "MOV reg, [...]") match = re.match(r"%s [^,]+, %s" % (INSTR_REGEX, DEREF_REGEX), asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidRead(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # MOVS* and SCAS* instructions (eg. "MOVSB" or "REP SCASD") match = re.search(r"^(?:REP(?:NZ)? )?(?PMOVS|SCAS)(?P[BWD])?", asm) if match: self.reason = self.movsInstr(fault_address, instr, match) return def movsInstr(self, fault_address, instr, match): operator = match.group("operator") suffix = match.group("suffix") size = {'B': 1, 'W': 2, 'D': 4}.get(suffix) error_cls = InvalidMemoryAcces try: process = self.process if CPU_64BITS: source_reg = 'rsi' dest_reg = 'rdi' else: source_reg = 'esi' dest_reg = 'edi' source_addr = process.getreg(source_reg) registers = {source_reg: source_addr} write = (operator == 'MOVS') if write: dest_addr = process.getreg(dest_reg) registers[dest_reg] = dest_addr if fault_address is not None: if fault_address == source_addr: error_cls = InvalidRead if write and fault_address == dest_addr: error_cls = InvalidWrite else: if write: fault_address = (source_addr, dest_addr) else: fault_address = (source_addr,) except PtraceError: registers = {} return error_cls(fault_address, size=size, instr=instr, registers=registers, process=self.process) def getSignalInfo(self): if RUNNING_LINUX: return self.process.getsiginfo() else: return None def memoryFault(self): # Get fault siginfo = self.getSignalInfo() if siginfo: fault_address = siginfo._sigfault._addr if not fault_address: fault_address = 0 else: fault_address = None # Get current instruction instr = self.getInstruction() # Call to invalid address? if fault_address is not None: try: ip = self.process.getInstrPointer() if ip == fault_address: self.reason = InstructionError(ip, process=self.process) return except PtraceError: pass # Stack overflow? stack = self.process.findStack() if stack: sp = self.process.getStackPointer() if not (stack.start <= sp <= stack.end): self.reason = StackOverflow(sp, stack, instr=instr, process=self.process) return # Guess error type using the assembler instruction if instr: self.memoryFaultInstr(instr, fault_address) if self.reason: return # Last chance: use generic invalid memory access error self.reason = InvalidMemoryAcces(fault_address, instr=instr, process=self.process) def mathError(self): instr = self.getInstruction() if not instr: return match = re.match(r"I?DIV (.*)", instr.text) if not match: return self.reason = DivisionByZero(instr=instr, process=self.process) def childExit(self): siginfo = self.getSignalInfo() if siginfo: child = siginfo._sigchld self.reason = ChildExit(child.pid, child.status, child.uid) else: self.reason = ChildExit() def display(self, log=None): self._analyze() if not log: log = error log("-" * 60) log("PID: %s" % self.process.pid) log("Signal: %s" % self.name) if self.reason: self.reason.display(log) log("-" * 60) python-ptrace-0.6.4/ptrace/debugger/memory_mapping.py0000644000175000017500000001001311572532406023237 0ustar haypohaypo00000000000000from ptrace.os_tools import HAS_PROC if HAS_PROC: from ptrace.linux_proc import openProc, ProcError from ptrace.debugger.process_error import ProcessError from ptrace.ctypes_tools import formatAddress import re from weakref import ref PROC_MAP_REGEX = re.compile( # Address range: '08048000-080b0000 ' r'([0-9a-f]+)-([0-9a-f]+) ' # Permission: 'r-xp ' r'(.{4}) ' # Offset: '0804d000' r'([0-9a-f]+) ' # Device (major:minor): 'fe:01 ' r'([0-9a-f]{2}):([0-9a-f]{2}) ' # Inode: '3334030' r'([0-9]+)' # Filename: ' /usr/bin/synergyc' r'(?: +(.*))?') class MemoryMapping(object): """ Process memory mapping (metadata about the mapping). Attributes: - start (int): first byte address - end (int): last byte address + 1 - permissions (str) - offset (int): for file, offset in bytes from the file start - major_device / minor_device (int): major / minor device number - inode (int) - pathname (str) - _process: weak reference to the process Operations: - "address in mapping" checks the address is in the mapping. - "search(somestring)" returns the offsets of "somestring" in the mapping - "str(mapping)" create one string describing the mapping - "repr(mapping)" create a string representation of the mapping, useful in list contexts """ def __init__(self, process, start, end, permissions, offset, major_device, minor_device, inode, pathname): self._process = ref(process) self.start = start self.end = end self.permissions = permissions self.offset = offset self.major_device = major_device self.minor_device = minor_device self.inode = inode self.pathname = pathname def __contains__(self, address): return self.start <= address < self.end def __str__(self): text = "%s-%s" % (formatAddress(self.start), formatAddress(self.end)) if self.pathname: text += " => %s" % self.pathname text += " (%s)" % self.permissions return text __repr__ = __str__ def search(self, bytestr): process = self._process() bytestr_len = len(bytestr) buf_len = 64 * 1024 if buf_len < bytestr_len: buf_len = bytestr_len remaining = self.end - self.start covered = self.start while remaining >= bytestr_len: if remaining > buf_len: requested = buf_len else: requested = remaining data = process.readBytes(covered, requested) if data == "": break offset = data.find(bytestr) if (offset == -1): skip = requested - bytestr_len + 1 else: yield (covered + offset) skip = offset + bytestr_len covered += skip remaining -= skip def readProcessMappings(process): """ Read all memory mappings of the specified process. Return a list of MemoryMapping objects, or empty list if it's not possible to read the mappings. May raise a ProcessError. """ maps = [] if not HAS_PROC: return maps try: mapsfile = openProc("%s/maps" % process.pid) except ProcError, err: raise ProcessError(process, "Unable to read process maps: %s" % err) try: for line in mapsfile: line = line.rstrip() match = PROC_MAP_REGEX.match(line) if not match: raise ProcessError(process, "Unable to parse memoy mapping: %r" % line) map = MemoryMapping( process, int(match.group(1), 16), int(match.group(2), 16), match.group(3), int(match.group(4), 16), int(match.group(5), 16), int(match.group(6), 16), int(match.group(7)), match.group(8)) maps.append(map) finally: mapsfile.close() return maps python-ptrace-0.6.4/ptrace/debugger/backtrace.py0000644000175000017500000000547611572532406022154 0ustar haypohaypo00000000000000from ptrace.ctypes_tools import formatAddress, formatWordHex from ptrace.cpu_info import CPU_WORD_SIZE, CPU_MAX_UINT from ptrace import PtraceError class BacktraceFrame(object): """ Backtrace frame. Attributes: - ip: instruction pointer - name: name of the function - arguments: value of the arguments """ def __init__(self, ip): self.ip = ip self.name = u"???" self.arguments = [] def __str__(self): arguments = (formatWordHex(arg) for arg in self.arguments) return u"IP=%s: %s (%s)" % (formatAddress(self.ip), self.name, ", ".join(arguments)) class Backtrace(object): """ Backtrace: all process frames since the start function. """ def __init__(self): self.frames = [] self.truncated = False def append(self, frame): self.frames.append(frame) def __iter__(self): return iter(self.frames) def __len__(self): return len(self.frames) def getBacktrace(process, max_args=6, max_depth=20): """ Get the current backtrace of the specified process: - max_args: maximum number of arguments in a frame - max_depth: maximum number of frames Return a Backtrace object. """ backtrace = Backtrace() # Get current instruction and frame pointer ip = process.getInstrPointer() fp = process.getFramePointer() depth = 0 while True: # Hit maximum trace depth? if max_depth <= depth: backtrace.truncated = True break # Read next frame pointer try: nextfp = process.readWord(fp) except PtraceError: nextfp = None # Guess number of function argument if fp and nextfp: nargs = ((nextfp - fp) // CPU_WORD_SIZE) - 2 nargs = min(nargs, max_args) else: nargs = 0 # Create frame frame = getBacktraceFrame(process, ip, fp, nargs) backtrace.append(frame) # End of the stack? if not nextfp: break # Move to next instruction/frame pointer ip = process.readWord(fp+CPU_WORD_SIZE) if ip == CPU_MAX_UINT: # Linux hack to detect end of the stack break fp = nextfp depth += 1 return backtrace def getBacktraceFrame(process, ip, fp, nargs): """ Get a backtrace frame: - ip: instruction pointer - fp: frame pointer - nargs: number of arguments Return a BacktraceFrame object. """ frame = BacktraceFrame(ip) address = fp + CPU_WORD_SIZE try: for index in xrange(nargs): address += CPU_WORD_SIZE word = process.readWord(address) frame.arguments.append(word) except PtraceError: # Ignore argument read error pass return frame python-ptrace-0.6.4/ptrace/debugger/child.py0000644000175000017500000000745611234417272021316 0ustar haypohaypo00000000000000""" Error pipe and serialization code comes from Python 2.5 subprocess module. """ from os import ( fork, execv, execve, waitpid, close, dup2, pipe, read, write, devnull) from sys import exc_info from traceback import format_exception from ptrace.os_tools import RUNNING_WINDOWS from subprocess import MAXFD from ptrace.binding import ptrace_traceme from ptrace import PtraceError from sys import exit from errno import EINTR import fcntl import pickle class ChildError(RuntimeError): pass class ChildPtraceError(ChildError): pass def _set_cloexec_flag(fd): if RUNNING_WINDOWS: return try: cloexec_flag = fcntl.FD_CLOEXEC except AttributeError: cloexec_flag = 1 old = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) def _waitpid_no_intr(pid, options): """Like os.waitpid, but retries on EINTR""" while True: try: return waitpid(pid, options) except OSError, e: if e.errno == EINTR: continue else: raise def _read_no_intr(fd, buffersize): """Like os.read, but retries on EINTR""" while True: try: return read(fd, buffersize) except OSError, e: if e.errno == EINTR: continue else: raise def _write_no_intr(fd, s): """Like os.write, but retries on EINTR""" while True: try: return write(fd, s) except OSError, e: if e.errno == EINTR: continue else: raise def _createParent(pid, errpipe_read): # Wait for exec to fail or succeed; possibly raising exception data = _read_no_intr(errpipe_read, 1048576) # Exceptions limited to 1 MB close(errpipe_read) if data: _waitpid_no_intr(pid, 0) child_exception = pickle.loads(data) raise child_exception def _createChild(arguments, no_stdout, env, errpipe_write): # Child code try: ptrace_traceme() except PtraceError, err: raise ChildError(str(err)) # Close all files except 0, 1, 2 and errpipe_write for fd in xrange(3, MAXFD): if fd == errpipe_write: continue try: close(fd) except OSError: pass try: _execChild(arguments, no_stdout, env) except: exc_type, exc_value, tb = exc_info() # Save the traceback and attach it to the exception object exc_lines = format_exception(exc_type, exc_value, tb) exc_value.child_traceback = ''.join(exc_lines) _write_no_intr(errpipe_write, pickle.dumps(exc_value)) exit(255) def _execChild(arguments, no_stdout, env): if no_stdout: try: null = open(devnull, 'wb') dup2(null.fileno(), 1) dup2(1, 2) null.close() except IOError, err: close(2) close(1) try: if env is not None: execve(arguments[0], arguments, env) else: execv(arguments[0], arguments) except Exception, err: raise ChildError(str(err)) def createChild(arguments, no_stdout, env=None): """ Create a child process: - arguments: list of string where (eg. ['ls', '-la']) - no_stdout: if True, use null device for stdout/stderr - env: environment variables dictionary Use: - env={} to start with an empty environment - env=None (default) to copy the environment """ errpipe_read, errpipe_write = pipe() _set_cloexec_flag(errpipe_write) # Fork process pid = fork() if pid: close(errpipe_write) _createParent(pid, errpipe_read) return pid else: close(errpipe_read) _createChild(arguments, no_stdout, env, errpipe_write) python-ptrace-0.6.4/ptrace/debugger/breakpoint.py0000644000175000017500000000370311572532406022362 0ustar haypohaypo00000000000000from ptrace.ctypes_tools import formatAddress from ptrace import PtraceError from logging import info from weakref import ref from ptrace.cpu_info import CPU_POWERPC, CPU_WORD_SIZE from ptrace.ctypes_tools import word2bytes from ptrace.six import b class Breakpoint(object): """ Software breakpoint. Use desinstall() method to remove the breakpoint from the process. """ def __init__(self, process, address, size=None): self._installed = False self.process = ref(process) self.address = address if CPU_POWERPC: size = CPU_WORD_SIZE elif size is None: size = 1 self.size = size # Store instruction bytes info("Install %s" % self) self.old_bytes = process.readBytes(address, size) if CPU_POWERPC: # Replace instruction with "TRAP" new_bytes = word2bytes(0x0cc00000) else: # Replace instruction with "INT 3" new_bytes = b("\xCC") * size process.writeBytes(address, new_bytes) self._installed = True def desinstall(self, set_ip=False): """ Remove the breakpoint from the associated process. If set_ip is True, restore the instruction pointer to the address of the breakpoint. """ if not self._installed: return self._installed = False info("Desinstall %s" % self) process = self.process() if not process: return if process.running: process.writeBytes(self.address, self.old_bytes) if set_ip: process.setInstrPointer(self.address) process.removeBreakpoint(self) def __str__(self): return "" % ( formatAddress(self.address), formatAddress(self.address + self.size - 1)) def __del__(self): try: self.desinstall(False) except PtraceError: pass python-ptrace-0.6.4/ptrace/mockup.py0000644000175000017500000000036711572532406017741 0ustar haypohaypo00000000000000""" Mockup classes used in unit tests. """ class FakeProcess(object): def __init__(self): self.regs = {} def setreg(self, name, value): self.regs[name] = value def getreg(self, name): return self.regs[name] python-ptrace-0.6.4/ptrace/compatibility.py0000644000175000017500000000157411234417272021313 0ustar haypohaypo00000000000000""" Compatibility functions for Python 2.4. any() function ============== any() returns True if at least one items is True, or False otherwise. >>> any([False, True]) True >>> any([True, True]) True >>> any([False, False]) False all() function ============== all() returns True if all items are True, or False otherwise. This function is just apply binary and operator (&) on all values. >>> all([True, True]) True >>> all([False, True]) False >>> all([False, False]) False """ import operator # --- any() from Python 2.5 --- try: from __builtin__ import any except ImportError: def any(items): for item in items: if item: return True return False # ---all() from Python 2.5 --- try: from __builtin__ import all except ImportError: def all(items): return reduce(operator.__and__, items) __all__ = ("any", "all") python-ptrace-0.6.4/ptrace/error.py0000644000175000017500000000350711234417272017571 0ustar haypohaypo00000000000000from sys import exc_info from traceback import format_exception from logging import ERROR, getLogger from ptrace.logging_tools import getLogFunc, changeLogLevel PTRACE_ERRORS = Exception def writeBacktrace(logger, log_level=ERROR): """ Write a backtrace into the logger with the specified log level. """ log_func = getLogFunc(logger, log_level) try: info = exc_info() trace = format_exception(*info) if trace[0] != "None\n": trace = ''.join(trace).rstrip() for line in trace.split("\n"): log_func(line.rstrip()) return except: pass log_func("Unable to get backtrace") def formatError(error): """ Format an error as a string. Write the error type as prefix. Eg. "[ValueError] invalid value". """ return "[%s] %s" % (error.__class__.__name__, error) def writeError(logger, error, title="ERROR", log_level=ERROR): """ Write an error into the logger: - logger: the logger (if None, use getLogger()) - error: the exception objet - title: error message prefix (eg. title="Initialization error") - log_level: log level of the error If the exception is a SystemExit or a KeyboardInterrupt, re-emit (raise) the exception and don't write it. """ if not logger: logger = getLogger() if error.__class__ in (SystemExit, KeyboardInterrupt): raise error log_func = getLogFunc(logger, log_level) log_func("%s: %s" % (title, formatError(error))) writeBacktrace(logger, log_level=changeLogLevel(log_level, -1)) class PtraceError(Exception): """ Ptrace error: have the optional attributes errno and pid. """ def __init__(self, message, errno=None, pid=None): Exception.__init__(self, message) self.errno = errno self.pid = pid python-ptrace-0.6.4/ptrace/logging_tools.py0000644000175000017500000000131611234417272021302 0ustar haypohaypo00000000000000from ptrace.tools import minmax from logging import ERROR, WARNING, INFO, DEBUG def getLogFunc(logger, level): """ Get the logger function for the specified logging level. """ if level == ERROR: return logger.error elif level == WARNING: return logger.warning elif level == INFO: return logger.info elif level == DEBUG: return logger.debug else: return logger.error def changeLogLevel(level, delta): """ Compute log level and make sure that the result is in DEBUG..ERROR. >>> changeLogLevel(ERROR, -1) == WARNING True >>> changeLogLevel(DEBUG, 1) == INFO True """ return minmax(DEBUG, level + delta*10, ERROR) python-ptrace-0.6.4/ptrace/profiler.py0000644000175000017500000000176311234417272020264 0ustar haypohaypo00000000000000from hotshot import Profile from hotshot.stats import load as loadStats from os import unlink try: from cStringIO import StringIO except ImportError: from StringIO import StringIO def runProfiler(logger, func, args=tuple(), kw={}, verbose=True, nb_func=25, sort_by=('time',)): """ Run a function in a profiler and then display the functions sorted by time. """ profile_filename = "/tmp/profiler" prof = Profile(profile_filename) try: logger.warning("Run profiler") result = prof.runcall(func, *args, **kw) prof.close() logger.error("Profiler: Process data...") stat = loadStats(profile_filename) stat.strip_dirs() stat.sort_stats(*sort_by) logger.error("Profiler: Result:") log = StringIO() stat.stream = log stat.print_stats(nb_func) log.seek(0) for line in log: logger.error(line.rstrip()) return result finally: unlink(profile_filename) python-ptrace-0.6.4/ptrace/tools.py0000644000175000017500000000771211234417272017602 0ustar haypohaypo00000000000000from ctypes import sizeof from ptrace.ctypes_tools import formatUintHex16, formatUintHex32, formatWordHex from datetime import datetime, timedelta from os import getenv, access, X_OK, pathsep, getcwd from os.path import join as path_join, isabs, dirname, normpath def dumpRegs(log, regs): """ Dump all registers using log callback (write one line). """ width = max( len(name) for name, type in regs._fields_ ) name_format = "%% %us" % width for name, type in regs._fields_: value = getattr(regs, name) name = name_format % name if sizeof(type) == 32: value = formatUintHex32(value) elif sizeof(type) == 16: value = formatUintHex16(value) else: value = formatWordHex(value) log("%s = %s" % (name, value)) def readBits(value, bitmasks): """ Extract bits from the integer value using a list of bit masks. bitmasks is a list of tuple (mask, text). >>> bitmask = ( ... (1, "exec"), ... (2, "write"), ... (4, "read")) ... >>> readBits(5, bitmask) ['exec', 'read'] """ bitset = [] for mask, item in bitmasks: if not value & mask: continue bitset.append(item) value = value & ~mask return bitset def formatBits(value, bitmasks, empty_text=None, format_value=str): """ Format a value using a bitmask: see readBits() functions. >>> bitmask = ( ... (1, "exec"), ... (2, "write"), ... (4, "read")) ... >>> formatBits(5, bitmask, empty_text="no permission") ' (5)' >>> formatBits(0, bitmask, empty_text="no permission") 'no permission' """ orig_value = value text = readBits(value, bitmasks) if text: text = "%s" % ("|".join(text)) if value: text = "<%s> (%s)" % (text, format_value(orig_value)) return text else: if empty_text: return empty_text else: return str(value) LOCAL_TIMEZONE_OFFSET = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # Start of UNIX timestamp (Epoch): 1st January 1970 at 00:00 UNIX_TIMESTAMP_T0 = datetime(1970, 1, 1) def timestampUNIX(value, is_local): """ Convert an UNIX (32-bit) timestamp to datetime object. Timestamp value is the number of seconds since the 1st January 1970 at 00:00. Maximum value is 2147483647: 19 january 2038 at 03:14:07. May raise ValueError for invalid value: value have to be in 0..2147483647. >>> timestampUNIX(0, False) datetime.datetime(1970, 1, 1, 0, 0) >>> timestampUNIX(1154175644.37, False) datetime.datetime(2006, 7, 29, 12, 20, 44, 370000) """ timestamp = UNIX_TIMESTAMP_T0 + timedelta(seconds=value) if is_local: timestamp += LOCAL_TIMEZONE_OFFSET return timestamp def locateProgram(program): """ Locate a program using the PATH environment variable. Return the unchanged program value if it's not possible to get the full program path. """ if isabs(program): return program if dirname(program): # ./test => $PWD/./test # ../python => $PWD/../python program = path_join(getcwd(), program) program = normpath(program) return program paths = getenv('PATH') if not paths: return program for path in paths.split(pathsep): filename = path_join(path, program) if access(filename, X_OK): return filename return program def minmax(min_value, value, max_value): """ Restrict value to [min_value; max_value] >>> minmax(-2, -3, 10) -2 >>> minmax(-2, 27, 10) 10 >>> minmax(-2, 0, 10) 0 """ return min(max(min_value, value), max_value) def inverseDict(data): """ Inverse a dictionary. >>> inverseDict({"0x10": 16, "0x20": 32}) {32: '0x20', 16: '0x10'} """ result = {} for key, value in data.iteritems(): result[value] = key return result python-ptrace-0.6.4/ptrace/syscall/0000775000175000017500000000000011722433414017533 5ustar haypohaypo00000000000000python-ptrace-0.6.4/ptrace/syscall/__init__.py0000644000175000017500000000040711347424720021646 0ustar haypohaypo00000000000000from ptrace.syscall.names import SYSCALL_NAMES, SOCKET_SYSCALL_NAMES from ptrace.syscall.prototypes import SYSCALL_PROTOTYPES, FILENAME_ARGUMENTS from ptrace.syscall.syscall_argument import SyscallArgument from ptrace.syscall.ptrace_syscall import PtraceSyscall python-ptrace-0.6.4/ptrace/syscall/freebsd_constants.py0000644000175000017500000000117411234417272023616 0ustar haypohaypo00000000000000from ptrace.syscall.posix_constants import SYSCALL_ARG_DICT RLIMIT_RESOURCE = { 0: "RLIMIT_CPU", 1: "RLIMIT_FSIZE", 2: "RLIMIT_DATA", 3: "RLIMIT_STACK", 4: "RLIMIT_CORE", 5: "RLIMIT_RSS", 6: "RLIMIT_MEMLOCK", 7: "RLIMIT_NPROC", 8: "RLIMIT_NOFILE", 9: "RLIMIT_SBSIZE", 10: "RLIMIT_VMEM", } SIGPROCMASK_HOW = {1:" SIG_BLOCK", 2: "SIG_UNBLOCK", 3: "SIG_SETMASK"} SYSCALL_ARG_DICT.update({ "getrlimit": {"resource": RLIMIT_RESOURCE}, "setrlimit": {"resource": RLIMIT_RESOURCE}, "sigprocmask": {"how": SIGPROCMASK_HOW}, "rt_sigprocmask": {"how": SIGPROCMASK_HOW}, }) python-ptrace-0.6.4/ptrace/syscall/prototypes.py0000644000175000017500000003204011347426707022344 0ustar haypohaypo00000000000000# From Linux kernel source code # include/linux/syscalls.h # arch/i386/kernel/syscall_table.S # arch/um/include/sysdep-i386/syscalls.h # arch/um/sys-i386/sys_call_table.S ALIASES = { "mmap": ("mmap2",), "break": ("brk",), "exit": ("exit_group",), "fcntl": ("fcntl64",), } # Name of arguments containing a filename or a path FILENAME_ARGUMENTS = set(("filename", "pathname")) SYSCALL_PROTOTYPES = { "read": ("ssize_t", ( ("unsigned int", "fd"), ("char*", "buf"), ("size_t", "count"), )), "write": ("ssize_t", ( ("int", "fd"), ("const char*", "buf"), ("size_t", "count"), )), "open": ("long", ( ("const char*", "filename"), ("int", "mode"), )), "readlink": ("long", ( ("const char*", "pathname"), ("char*", "buf"), ("int", "bufsize"), )), "close": ("long", ( ("unsigned int", "fd"), )), "set_tid_address": ("long", ( ("int*", "tidptr"), )), "set_robust_list": ("long", ( ("struct robust_list_head*", "head"), ("size_t", "len_ptr"), )), "fcntl": ("long", ( ("unsigned int", "fd"), ("unsigned int", "cmd"), ("unsigned int", "arg"), )), "stat": ("long", ( ("const char*", "filename"), ("struct oldstat*", "statbuf"), )), "stat64": ("long", ( ("const char*", "filename"), ("struct stat64*", "statbuf"), )), "lstat": ("long", ( ("const char*", "filename"), ("struct oldstat*", "statbuf"), )), "lstat64": ("long", ( ("const char*", "filename"), ("struct stat64*", "statbuf"), )), "fstat": ("long", ( ("unsigned int", "fd"), ("struct oldstat*", "statbuf"), )), "fstat64": ("long", ( ("unsigned long", "fd"), ("struct stat64*", "buf"), )), "fstatat64": ("long", ( ("unsigned long", "dirfd"), ("const char*", "filename"), ("struct stat64*", "statbuf"), ("int", "flags"), )), "statfs": ("long", ( ("const char*", "pathname"), ("struct statfs*", "buf"), )), "fstatfs": ("long", ( ("int", "fs"), ("struct statfs*", "buf"), )), "access": ("long", ( ("char*", "filename"), ("int", "mode"), )), "lseek": ("long", ( ("unsigned int", "fd"), ("unsigned long", "offset"), ("loff_t*", "result"), ("unsigned int", "origin"), )), "llseek": ("long", ( ("unsigned int", "fd"), ("unsigned long", "offset_high"), ("unsigned long", "offset_low"), ("loff_t*", "result"), ("unsigned int", "origin"), )), "break": ("void*", ( ("void*", "brk"), )), "sigprocmask": ("int", ( ("int", "how"), ("const old_sigset_t*", "set"), ("old_sigset_t*", "ofset"), )), "rt_sigprocmask": ("int", ( ("int", "how"), ("const sigset_t*", "set"), ("sigset_t*", "ofset"), ("size_t", "sigsetsize"), )), "sigaction": ("long", ( ("int", "signum"), ("const struct oldsigaction*", "act"), ("struct oldsigaction*", "oldact"), )), "rt_sigaction": ("long", ( # FIXME: Check parameters! ("int", "signum"), ("const struct sigaction*", "act"), ("struct sigaction*", "oldact"), ("int", "sigsetsize"), )), "select": ("long", ( ("int", "n"), ("fd_set*", "inp"), ("fd_set*", "outp"), ("fd_set*", "exp"), ("struct timeval*", "timeout"), )), "poll": ("long", ( ("struct pollfd*", "ufds"), ("int", "nfds"), ("long", "timeout"), )), "gettimeofday": ("long", ( ("struct timeval*", "tv"), ("struct timezone*", "tz"), )), "settimeofday": ("long", ( ("struct timeval*", "tv"), ("struct timezone*", "tz"), )), "socketcall": ("long", ( ("int", "call"), ("unsigned long*", "args"), )), "clock_gettime": ("long", ( ("clockid_t", "which_clock"), ("struct timespec*", "tp"), )), "clock_getres": ("long", ( ("clockid_t", "which_clock"), ("struct timespec*", "tp"), )), "time": ("time_t", ( ("time_t*", "tloc"), )), "stime": ("long", ( ("time_t*", "tptr"), )), "munmap": ("long", ( ("void*", "addr"), ("size_t", "length"), )), "mmap": ("void*", ( ("void*", "start"), ("unsigned long", "length"), ("unsigned long", "prot"), ("long", "flags"), ("long", "fd"), ("unsigned long", "offset"), )), "madvise": ("long", ( ("unsigned long", "start"), ("size_t", "length"), ("int", "behaviour"), )), "exit": ("void", ( ("int", "error_code"), )), "futex": ("long", ( ("u32*", "uaddr"), ("int", "op"), ("u32", "val"), ("struct timespec*", "utime"), ("u32*", "uaddr2"), ("u32", "val3"), )), "ioctl": ("long", ( ("unsigned int", "fd"), ("unsigend int", "cmd"), ("void*", "arg"), )), "getrusage": ("long", ( ("int", "who"), ("struct rusage*", "usage"), )), "times": ("clock_t", ( ("struct tms*", "tbuf"), )), "mprotect": ("long", ( ("void*", "start"), ("size_t", "len"), ("unsigned long", "prot"), )), "getrlimit": ("int", ( ("unsigned int", "resource"), ("struct rlimit*", "rlim"), )), "setrlimit": ("int", ( ("unsigned int", "resource"), ("struct rlimit*", "rlim"), )), "getuid": ("uid_t", tuple()), "geteuid": ("uid_t", tuple()), "getuid16": ("uid16_t", tuple()), "geteuid16": ("uid16_t", tuple()), "issetugid": ("long", tuple()), "getgid": ("gid_t", tuple()), "getegid": ("gid_t", tuple()), "getgid16": ("gid16_t", tuple()), "getegid16": ("gid16_t", tuple()), "getpid": ("pid_t", tuple()), "getppid": ("pid_t", tuple()), "setuid": ("long", (("uid_t", "uid"),)), "setreuid": ("long", (("uid_t", "uid"),)), "setfsuid": ("long", (("uid_t", "uid"),)), "setgid": ("long", (("gid_t", "gid"),)), "setregid": ("long", (("gid_t", "gid"),)), "setfsgid": ("long", (("gid_t", "gid"),)), "getsid": ("long", (("pid_t", "pid"),)), "setsid": ("long", tuple()), "pipe": ("int", ( ("int[2]", "filedes"), )), "wait4": ("pid_t", ( ("pid_t", "pid"), ("int*", "status"), ("int", "options"), ("struct rusage*", "rusage"), )), "waitpid": ("pid_t", ( ("pid_t", "pid"), ("int*", "status"), ("int", "options"), )), "set_thread_area": ("long", ( ("struct user_desc*", "u_info"), )), "oldolduname": ("long", ( ("struct oldold_utsname*", "name"), )), "olduname": ("long", ( ("struct old_utsname*", "name"), )), "uname": ("long", ( ("struct new_utsname*", "name"), )), "clone": ("long", ( ("int", "flags"), ("void*", "child_stack"), ("void*", "parent_tidptr"), ("struct user_desc*", "newtls"), ("void*", "child_tidptr"), )), "__getcwd": ("long", ( ("char*", "pathname"), ("size_t", "size"), )), "dup2": ("long", ( ("int", "fd"), ("int", "fd2"), )), "fork": ("uid_t", tuple()), "execve": ("long", ( ("const char*", "filename"), ("const char**", "argv"), ("const char**", "envp"), )), "readv": ("ssize_t", ( ("int", "fd"), ("const iovec*", "vector"), ("int", "count"), )), "writev": ("ssize_t", ( ("int", "fd"), ("const iovec*", "vector"), ("int", "count"), )), "openat": ("long", ( ("int", "dirfd"), ("const char*", "pathname"), ("int", "flags"), ("int", "mode"), )), "getdents": ("long", ( ("int", "fd"), ("struct dirent*", "dirp"), ("unsigned int", "count"), )), "getdents64": ("long", ( ("int", "fd"), ("struct dirent64*", "dirp"), ("unsigned int", "count"), )), "dup": ("long", ( ("int", "fd"), )), "dup2": ("long", ( ("int", "oldfd"), ("int", "newfd"), )), "fchdir": ("long", ( ("int", "fd"), )), "getdirentries": ("long", ( ("int", "fd"), ("void*", "buf"), ("int", "nbytes"), ("long*", "basep"), )), "unlink": ("long", ( ("const char*", "pathname"), )), "kill" : ("long", ( ("int", "pid"), ("int", "signum"), )), "modify_ldt" : ("long", ( ("int", "func"), ("void*", "ptr"), ("unsigned long", "bytecount"), )), "ipc" : ("long", ( ("unsigned int", "call"), ("int", "first"), ("unsigned long", "second"), ("long", "third"), ("void*", "ptr"), ("long", "fifth"), )), "nanosleep" : ("long", ( ("struct timespec*", "rqtp"), ("struct timespec*", "rmtp"), )), "restart_syscall" : ("long", tuple()), "getsockname" : ("long", ( ("int", "fd"), ("struct sockaddr*", "name"), ("socklen_t*", "namelen"), )), "getpeername" : ("long", ( ("int", "fd"), ("struct sockaddr*", "name"), ("socklen_t*", "namelen"), )), "getsockopt" : ("long", ( ("int", "fd"), ("int", "level"), ("int", "optname"), ("void*", "optval"), ("socklen_t*", "optlen"), )), "setsockopt" : ("long", ( ("int", "fd"), ("int", "level"), ("int", "optname"), ("void*", "optval"), ("socklen_t*", "optlen"), )), "bind" : ("long", ( ("int", "fd"), ("const struct sockaddr*", "addr"), ("socklen_t", "addrlen"), )), "connect" : ("long", ( ("int", "fd"), ("const struct sockaddr*", "addr"), ("socklen_t", "addrlen"), )), "socket" : ("long", ( ("int", "domain"), ("int", "type"), ("int", "protocol"), )), "alarm" : ("long", ( ("unsigned int", "seconds"), )), "recv": ("ssize_t", ( ("int", "sockfd"), ("void*", "buf"), ("size_t", "len"), ("int", "flags"), )), "recvfrom": ("ssize_t", ( ("int", "sockfd"), ("void*", "buf"), ("size_t", "len"), ("int", "flags"), ("struct sockaddr*", "src_addr"), ("socklen_t", "addrlen"), )), "recvmsg": ("ssize_t", ( ("int", "sockfd"), ("struct msghdr*", "msg"), ("int", "flags"), )), "send": ("ssize_t", ( ("int", "sockfd"), ("const void*", "buf"), ("size_t", "len"), ("int", "flags"), )), "sendto": ("ssize_t", ( ("int", "sockfd"), ("const void*", "buf"), ("size_t", "len"), ("int", "flags"), ("const struct sockaddr*", "dest_addr"), ("socklen_t", "addrlen"), )), "sendmsg": ("ssize_t", ( ("int", "sockfd"), ("const struct msghdr*", "buf"), ("int", "flags"), )), "listen": ("int", ( ("int", "fd"), ("int", "backlog"), )), "accept": ("int", ( ("int", "fd"), ("struct sockaddr*", "addr"), ("socklen_t*", "addrlen"), )), "socketpair": ("int", ( ("int", "family"), ("int", "type"), ("int", "protocol"), ("int*", "sockvec"), )), "shutdown": ("int", ( ("int", "fd"), ("int", "how"), )), } for orig, copies in ALIASES.iteritems(): orig = SYSCALL_PROTOTYPES[orig] for copy in copies: SYSCALL_PROTOTYPES[copy] = orig python-ptrace-0.6.4/ptrace/syscall/posix_arg.py0000644000175000017500000000425711234417272022110 0ustar haypohaypo00000000000000from ptrace.tools import readBits, formatBits from ptrace.signames import signalName # From /usr/include/bits/mman.h (Ubuntu Feisty, i386) MMAP_PROT_BITMASK = ( (1, "PROT_READ"), (2, "PROT_WRITE"), (4, "PROT_EXEC"), (0x01000000, "PROT_GROWSDOWN"), (0x02000000, "PROT_GROWSUP"), ) def formatMmapProt(argument): return formatBits(argument.value, MMAP_PROT_BITMASK, "PROT_NONE") # From /usr/include/bits/mman.h (Ubuntu Feisty, i386) ACCESS_MODE_BITMASK = ( (1, "X_OK"), (2, "W_OK"), (4, "R_OK"), ) def formatAccessMode(argument): return formatBits(argument.value, ACCESS_MODE_BITMASK, "F_OK") # From /usr/include/bits/fcntl.h (Ubuntu Feisty, i386) OPEN_MODE_BITMASK = ( (01, "O_WRONLY"), (02, "O_RDWR"), (0100, "O_CREAT"), (0200, "O_EXCL"), (0400, "O_NOCTTY"), (01000, "O_TRUNC"), (02000, "O_APPEND"), (04000, "O_NONBLOCK"), (010000, "O_SYNC"), (020000, "O_ASYNC"), (040000, "O_DIRECT"), (0100000, "O_LARGEFILE"), (0200000, "O_DIRECTORY"), (0400000, "O_NOFOLLOW"), (01000000, "O_NOATIME"), ) def formatOpenMode(argument): return formatBits(int(argument.value), OPEN_MODE_BITMASK, "O_RDONLY", oct) CLONE_FLAGS_BITMASK = ( (0x00000100, "CLONE_VM"), (0x00000200, "CLONE_FS"), (0x00000400, "CLONE_FILES"), (0x00000800, "CLONE_SIGHAND"), (0x00002000, "CLONE_PTRACE"), (0x00004000, "CLONE_VFORK"), (0x00008000, "CLONE_PARENT"), (0x00010000, "CLONE_THREAD"), (0x00020000, "CLONE_NEWNS"), (0x00040000, "CLONE_SYSVSEM"), (0x00080000, "CLONE_SETTLS"), (0x00100000, "CLONE_PARENT_SETTID"), (0x00200000, "CLONE_CHILD_CLEARTID"), (0x00400000, "CLONE_DETACHED"), (0x00800000, "CLONE_UNTRACED"), (0x01000000, "CLONE_CHILD_SETTID"), (0x02000000, "CLONE_STOPPED"), (0x04000000, "CLONE_NEWUTS"), (0x08000000, "CLONE_NEWIPC"), ) def formatCloneFlags(argument): flags = argument.value bits = readBits(flags, CLONE_FLAGS_BITMASK) signum = flags & 0xFF if signum: bits.insert(0, signalName(signum)) if bits: bits = "%s" % ("|".join(bits)) return "<%s> (%s)" % (bits, str(flags)) else: return str(flags) python-ptrace-0.6.4/ptrace/syscall/socketcall_constants.py0000644000175000017500000000252311347426707024337 0ustar haypohaypo00000000000000SOCKETCALL = { 1: "socket", 2: "bind", 3: "connect", 4: "listen", 5: "accept", 6: "getsockname", 7: "getpeername", 8: "socketpair", 9: "send", 10: "recv", 11: "sendto", 12: "recvfrom", 13: "shutdown", 14: "setsockopt", 15: "getsockopt", 16: "sendmsg", 17: "recvmsg", } SOCKET_FAMILY = { 0: "AF_UNSPEC", 1: "AF_FILE", 2: "AF_INET", 3: "AF_AX25", 4: "AF_IPX", 5: "AF_APPLETALK", 6: "AF_NETROM", 7: "AF_BRIDGE", 8: "AF_ATMPVC", 9: "AF_X25", 10: "AF_INET6", 11: "AF_ROSE", 12: "AF_DECnet", 13: "AF_NETBEUI", 14: "AF_SECURITY", 15: "AF_KEY", 16: "AF_NETLINK", 17: "AF_PACKET", 18: "AF_ASH", 19: "AF_ECONET", 20: "AF_ATMSVC", 22: "AF_SNA", 23: "AF_IRDA", 24: "AF_PPPOX", 25: "AF_WANPIPE", 31: "AF_BLUETOOTH", } SOCKET_TYPE = { 1: "SOCK_STREAM", 2: "SOCK_DGRAM", 3: "SOCK_RAW", 4: "SOCK_RDM", 5: "SOCK_SEQPACKET", 10: "SOCK_PACKET", } SOCKET_PROTOCOL = { 1: "IPPROTO_ICMP", 58: "IPPROTO_ICMPV6", } SETSOCKOPT_LEVEL = { 0: "SOL_IP", 1: "SOL_SOCKET", } SETSOCKOPT_OPTNAME = { # level 0 (SOL_IP) 1: "IP_TOS", # level 1 (SOL_SOCKET) 2: "SO_REUSEADDR", 9: "SO_KEEPALIVE", 20: "SO_RCVTIMEO", 21: "SO_SNDTIMEO", } python-ptrace-0.6.4/ptrace/syscall/linux_syscall64.py0000644000175000017500000001401311347424720023150 0ustar haypohaypo00000000000000# Linux kernel 2.6.23 on Intel Core2 Duo E6400 SYSCALL_NAMES = { 0: "read", 1: "write", 2: "open", 3: "close", 4: "stat", 5: "fstat", 6: "lstat", 7: "poll", 8: "lseek", 9: "mmap", 10: "mprotect", 11: "munmap", 12: "brk", 13: "rt_sigaction", 14: "rt_sigprocmask", 15: "rt_sigreturn", 16: "ioctl", 17: "pread64", 18: "pwrite64", 19: "readv", 20: "writev", 21: "access", 22: "pipe", 23: "select", 24: "sched_yield", 25: "mremap", 26: "msync", 27: "mincore", 28: "madvise", 29: "shmget", 30: "shmat", 31: "shmctl", 32: "dup", 33: "dup2", 34: "pause", 35: "nanosleep", 36: "getitimer", 37: "alarm", 38: "setitimer", 39: "getpid", 40: "sendfile", 41: "socket", 42: "connect", 43: "accept", 44: "sendto", 45: "recvfrom", 46: "sendmsg", 47: "recvmsg", 48: "shutdown", 49: "bind", 50: "listen", 51: "getsockname", 52: "getpeername", 53: "socketpair", 54: "setsockopt", 55: "getsockopt", 56: "clone", 57: "fork", 58: "vfork", 59: "execve", 60: "exit", 61: "wait4", 62: "kill", 63: "uname", 64: "semget", 65: "semop", 66: "semctl", 67: "shmdt", 68: "msgget", 69: "msgsnd", 70: "msgrcv", 71: "msgctl", 72: "fcntl", 73: "flock", 74: "fsync", 75: "fdatasync", 76: "truncate", 77: "ftruncate", 78: "getdents", 79: "getcwd", 80: "chdir", 81: "fchdir", 82: "rename", 83: "mkdir", 84: "rmdir", 85: "creat", 86: "link", 87: "unlink", 88: "symlink", 89: "readlink", 90: "chmod", 91: "fchmod", 92: "chown", 93: "fchown", 94: "lchown", 95: "umask", 96: "gettimeofday", 97: "getrlimit", 98: "getrusage", 99: "sysinfo", 100: "times", 101: "ptrace", 102: "getuid", 103: "syslog", 104: "getgid", 105: "setuid", 106: "setgid", 107: "geteuid", 108: "getegid", 109: "setpgid", 110: "getppid", 111: "getpgrp", 112: "setsid", 113: "setreuid", 114: "setregid", 115: "getgroups", 116: "setgroups", 117: "setresuid", 118: "getresuid", 119: "setresgid", 120: "getresgid", 121: "getpgid", 122: "setfsuid", 123: "setfsgid", 124: "getsid", 125: "capget", 126: "capset", 127: "rt_sigpending", 128: "rt_sigtimedwait", 129: "rt_sigqueueinfo", 130: "rt_sigsuspend", 131: "sigaltstack", 132: "utime", 133: "mknod", 134: "uselib", 135: "personality", 136: "ustat", 137: "statfs", 138: "fstatfs", 139: "sysfs", 140: "getpriority", 141: "setpriority", 142: "sched_setparam", 143: "sched_getparam", 144: "sched_setscheduler", 145: "sched_getscheduler", 146: "sched_get_priority_max", 147: "sched_get_priority_min", 148: "sched_rr_get_interval", 149: "mlock", 150: "munlock", 151: "mlockall", 152: "munlockall", 153: "vhangup", 154: "modify_ldt", 155: "pivot_root", 156: "_sysctl", 157: "prctl", 158: "arch_prctl", 159: "adjtimex", 160: "setrlimit", 161: "chroot", 162: "sync", 163: "acct", 164: "settimeofday", 165: "mount", 166: "umount2", 167: "swapon", 168: "swapoff", 169: "reboot", 170: "sethostname", 171: "setdomainname", 172: "iopl", 173: "ioperm", 174: "create_module", 175: "init_module", 176: "delete_module", 177: "get_kernel_syms", 178: "query_module", 179: "quotactl", 180: "nfsservctl", 181: "getpmsg", 182: "putpmsg", 183: "afs_syscall", 184: "tuxcall", 185: "security", 186: "gettid", 187: "readahead", 188: "setxattr", 189: "lsetxattr", 190: "fsetxattr", 191: "getxattr", 192: "lgetxattr", 193: "fgetxattr", 194: "listxattr", 195: "llistxattr", 196: "flistxattr", 197: "removexattr", 198: "lremovexattr", 199: "fremovexattr", 200: "tkill", 201: "time", 202: "futex", 203: "sched_setaffinity", 204: "sched_getaffinity", 205: "set_thread_area", 206: "io_setup", 207: "io_destroy", 208: "io_getevents", 209: "io_submit", 210: "io_cancel", 211: "get_thread_area", 212: "lookup_dcookie", 213: "epoll_create", 214: "epoll_ctl_old", 215: "epoll_wait_old", 216: "remap_file_pages", 217: "getdents64", 218: "set_tid_address", 219: "restart_syscall", 220: "semtimedop", 221: "fadvise64", 222: "timer_create", 223: "timer_settime", 224: "timer_gettime", 225: "timer_getoverrun", 226: "timer_delete", 227: "clock_settime", 228: "clock_gettime", 229: "clock_getres", 230: "clock_nanosleep", 231: "exit_group", 232: "epoll_wait", 233: "epoll_ctl", 234: "tgkill", 235: "utimes", 236: "vserver", 237: "mbind", 238: "set_mempolicy", 239: "get_mempolicy", 240: "mq_open", 241: "mq_unlink", 242: "mq_timedsend", 243: "mq_timedreceive", 244: "mq_notify", 245: "mq_getsetattr", 246: "kexec_load", 247: "waitid", 248: "add_key", 249: "request_key", 250: "keyctl", 251: "ioprio_set", 252: "ioprio_get", 253: "inotify_init", 254: "inotify_add_watch", 255: "inotify_rm_watch", 256: "migrate_pages", 257: "openat", 258: "mkdirat", 259: "mknodat", 260: "fchownat", 261: "futimesat", 262: "newfstatat", 263: "unlinkat", 264: "renameat", 265: "linkat", 266: "symlinkat", 267: "readlinkat", 268: "fchmodat", 269: "faccessat", 270: "pselect6", 271: "ppoll", 272: "unshare", 273: "set_robust_list", 274: "get_robust_list", 275: "splice", 276: "tee", 277: "sync_file_range", 278: "vmsplice", 279: "move_pages", 280: "utimensat", 281: "epoll_pwait", 282: "signalfd", 283: "timerfd", 284: "eventfd", 285: "fallocate", } SOCKET_SYSCALL_NAMES = set(( "socket", "socketpair", "connect", "sendto", "recvfrom", "sendmsg", "recvmsg", "bind", "listen", "accept", "getsockname", "getpeername", "getsockopt", "setsockopt", "shutdown", )) python-ptrace-0.6.4/ptrace/syscall/linux_struct.py0000644000175000017500000000231111234417272022645 0ustar haypohaypo00000000000000from ctypes import (Structure, c_char, c_short, c_int, c_uint, c_long, c_ulong) time_t = c_long suseconds_t = c_long rlim_t = c_long class timeval(Structure): _fields_ = ( ("tv_sec", time_t), ("tv_usec", suseconds_t), ) class timespec(Structure): _fields_ = ( ("tv_sec", time_t), ("tv_nsec", c_long), ) class pollfd(Structure): _fields_ = ( ("fd", c_int), ("events", c_short), ("revents", c_short), ) class rlimit(Structure): _fields_ = ( ("rlim_cur", rlim_t), ("rlim_max", rlim_t), ) class new_utsname(Structure): _fields_ = ( ("sysname", c_char*65), ("nodename", c_char*65), ("release", c_char*65), ("version", c_char*65), ("machine", c_char*65), ("domainname", c_char*65), ) # Arch depend class user_desc(Structure): _fields_ = ( ("entry_number", c_uint), ("base_addr", c_ulong), ("limit", c_uint), ("_bits_", c_char), # unsigned int seg_32bit:1; # unsigned int contents:2; # unsigned int read_exec_only:1; # unsigned int limit_in_pages:1; # unsigned int seg_not_present:1; # unsigned int useable:1; ) python-ptrace-0.6.4/ptrace/syscall/linux_syscall32.py0000644000175000017500000001556511347424720023160 0ustar haypohaypo00000000000000# Linux kernel syscall list from Linux 2.6.21 for i386 # # List extracted from Linux kernel source code, see: # arch/i386/kernel/syscall_table.S SYSCALL_NAMES = { 0: "restart_syscall", 1: "exit", 2: "fork", 3: "read", 4: "write", 5: "open", 6: "close", 7: "waitpid", 8: "creat", 9: "link", 10: "unlink", 11: "execve", 12: "chdir", 13: "time", 14: "mknod", 15: "chmod", 16: "lchown16", # 17: - 18: "stat", 19: "lseek", 20: "getpid", 21: "mount", 22: "oldumount", 23: "setuid16", 24: "getuid16", 25: "stime", 26: "ptrace", 27: "alarm", 28: "fstat", 29: "pause", 30: "utime", # 31: - # 32: - 33: "access", 34: "nice", # 35: - 36: "sync", 37: "kill", 38: "rename", 39: "mkdir", 40: "rmdir", 41: "dup", 42: "pipe", 43: "times", # 44: - 45: "brk", 46: "setgid16", 47: "getgid16", 48: "signal", 49: "geteuid16", 50: "getegid16", 51: "acct", 52: "umount", # 53: - 54: "ioctl", 55: "fcntl", # 56: - 57: "setpgid", # 58: - 59: "oldolduname", 60: "umask", 61: "chroot", 62: "ustat", 63: "dup2", 64: "getppid", 65: "getpgrp", 66: "setsid", 67: "sigaction", 68: "sgetmask", 69: "ssetmask", 70: "setreuid16", 71: "setregid16", 72: "sigsuspend", 73: "sigpending", 74: "sethostname", 75: "setrlimit", 76: "old_getrlimit", 77: "getrusage", 78: "gettimeofday", 79: "settimeofday", 80: "getgroups16", 81: "setgroups16", 82: "old_select", 83: "symlink", 84: "lstat", 85: "readlink", 86: "uselib", 87: "swapon", 88: "reboot", 89: "old_readdir", 90: "old_mmap", 91: "munmap", 92: "truncate", 93: "ftruncate", 94: "fchmod", 95: "fchown16", 96: "getpriority", 97: "setpriority", # 98: - 99: "statfs", 100: "fstatfs", 101: "ioperm", 102: "socketcall", 103: "syslog", 104: "setitimer", 105: "getitimer", 106: "newstat", 107: "newlstat", 108: "newfstat", 109: "olduname", 110: "iopl", 111: "vhangup", # 112: old "idle" 113: "vm86old", 114: "wait4", 115: "swapoff", 116: "sysinfo", 117: "ipc", 118: "fsync", 119: "sigreturn", 120: "clone", 121: "setdomainname", 122: "uname", 123: "modify_ldt", 124: "adjtimex", 125: "mprotect", 126: "sigprocmask", # 127: old "create_module" 128: "init_module", 129: "delete_module", # 130: old "get_kernel_syms" 131: "quotactl", 132: "getpgid", 133: "fchdir", 134: "bdflush", 135: "sysfs", 136: "personality", # 137: reserved for afs_syscall 138: "setfsuid16", 139: "setfsgid16", 140: "llseek", 141: "getdents", 142: "select", 143: "flock", 144: "msync", 145: "readv", 146: "writev", 147: "getsid", 148: "fdatasync", 149: "sysctl", 150: "mlock", 151: "munlock", 152: "mlockall", 153: "munlockall", 154: "sched_setparam", 155: "sched_getparam", 156: "sched_setscheduler", 157: "sched_getscheduler", 158: "sched_yield", 159: "sched_get_priority_max", 160: "sched_get_priority_min", 161: "sched_rr_get_interval", 162: "nanosleep", 163: "mremap", 164: "setresuid16", 165: "getresuid16", 166: "vm86", # 167: old "query_module" 168: "poll", 169: "nfsservctl", 170: "setresgid16", 171: "getresgid16", 172: "prctl", 173: "rt_sigreturn", 174: "rt_sigaction", 175: "rt_sigprocmask", 176: "rt_sigpending", 177: "rt_sigtimedwait", 178: "rt_sigqueueinfo", 179: "rt_sigsuspend", 180: "pread64", 181: "pwrite64", 182: "chown16", 183: "getcwd", 184: "capget", 185: "capset", 186: "sigaltstack", 187: "sendfile", # 188: (reserved) # 189: (reserved) 190: "vfork", 191: "getrlimit", 192: "mmap2", 193: "truncate64", 194: "ftruncate64", 195: "stat64", 196: "lstat64", 197: "fstat64", 198: "lchown", 199: "getuid", 200: "getgid", 201: "geteuid", 202: "getegid", 203: "setreuid", 204: "setregid", 205: "getgroups", 206: "setgroups", 207: "fchown", 208: "setresuid", 209: "getresuid", 210: "setresgid", 211: "getresgid", 212: "chown", 213: "setuid", 214: "setgid", 215: "setfsuid", 216: "setfsgid", # ------------------------------- 217: "pivot_root", 218: "mincore", 219: "madvise", 220: "getdents64", 221: "fcntl64", # 222: - # 223: - 224: "gettid", 225: "readahead", 226: "setxattr", 227: "lsetxattr", 228: "fsetxattr", 229: "getxattr", 230: "lgetxattr", 231: "fgetxattr", 232: "listxattr", 233: "llistxattr", 234: "flistxattr", 235: "removexattr", 236: "lremovexattr", 237: "fremovexattr", 238: "tkill", 239: "sendfile64", 240: "futex", 241: "sched_setaffinity", 242: "sched_getaffinity", 243: "set_thread_area", 244: "get_thread_area", 245: "io_setup", 246: "io_destroy", 247: "io_getevents", 248: "io_submit", 249: "io_cancel", 250: "fadvise64", # 251: - 252: "exit_group", 253: "lookup_dcookie", 254: "epoll_create", 255: "epoll_ctl", 256: "epoll_wait", 257: "remap_file_pages", 258: "set_tid_address", 259: "timer_create", 260: "timer_settime", 261: "timer_gettime", 262: "timer_getoverrun", 263: "timer_delete", 264: "clock_settime", 265: "clock_gettime", 266: "clock_getres", 267: "clock_nanosleep", 268: "statfs64", 269: "fstatfs64", 270: "tgkill", 271: "utimes", 272: "fadvise64_64", # 273: - 274: "mbind", 275: "get_mempolicy", 276: "set_mempolicy", 277: "mq_open", 278: "mq_unlink", 279: "mq_timedsend", 280: "mq_timedreceive", 281: "mq_notify", 282: "mq_getsetattr", 283: "kexec_load", 284: "waitid", # 285: - 286: "add_key", 287: "request_key", 288: "keyctl", 289: "ioprio_set", 290: "ioprio_get", 291: "inotify_init", 292: "inotify_add_watch", 293: "inotify_rm_watch", 294: "migrate_pages", 295: "openat", 296: "mkdirat", 297: "mknodat", 298: "fchownat", 299: "futimesat", 300: "fstatat64", 301: "unlinkat", 302: "renameat", 303: "linkat", 304: "symlinkat", 305: "readlinkat", 306: "fchmodat", 307: "faccessat", 308: "pselect6", 309: "ppoll", 310: "unshare", 311: "set_robust_list", 312: "get_robust_list", 313: "splice", 314: "sync_file_range", 315: "tee", 316: "vmsplice", 317: "move_pages", 318: "getcpu", 319: "epoll_pwait", } SOCKET_SYSCALL_NAMES = set(("socketcall",)) python-ptrace-0.6.4/ptrace/syscall/ptrace_syscall.py0000644000175000017500000001103011234417272023110 0ustar haypohaypo00000000000000from os import strerror from itertools import izip from ptrace.cpu_info import CPU_X86_64, CPU_POWERPC, CPU_I386 from ptrace.ctypes_tools import ulong2long, formatAddress, formatWordHex from ptrace.func_call import FunctionCall from ptrace.syscall import SYSCALL_NAMES, SYSCALL_PROTOTYPES, SyscallArgument from ptrace.syscall.socketcall import setupSocketCall from ptrace.os_tools import RUNNING_LINUX, RUNNING_BSD from ptrace.cpu_info import CPU_WORD_SIZE from ptrace.binding.cpu import CPU_INSTR_POINTER PREFORMAT_ARGUMENTS = { "select": (2, 3, 4), "execve": (0, 1, 2), "clone": (0, 1), } class PtraceSyscall(FunctionCall): def __init__(self, process, options, regs=None): FunctionCall.__init__(self, "syscall", options, SyscallArgument) self.process = process self.restype = "long" self.result = None self.result_text = None self.instr_pointer = None if not regs: regs = self.process.getregs() self.readSyscall(regs) def enter(self, regs=None): if not regs: regs = self.process.getregs() argument_values = self.readArgumentValues(regs) self.readArguments(argument_values) if self.name == "socketcall" and self.options.replace_socketcall: setupSocketCall(self, self.process, self[0], self[1].value) # Some arguments are lost after the syscall, so format them now if self.name in PREFORMAT_ARGUMENTS: for index in PREFORMAT_ARGUMENTS[self.name]: argument = self.arguments[index] argument.format() if self.options.instr_pointer: self.instr_pointer = getattr(regs, CPU_INSTR_POINTER) def readSyscall(self, regs): # Read syscall number if CPU_POWERPC: self.syscall = regs.gpr0 elif RUNNING_LINUX: if CPU_X86_64: self.syscall = regs.orig_rax else: self.syscall = regs.orig_eax else: self.syscall = regs.eax # Get syscall variables self.name = SYSCALL_NAMES.get(self.syscall, "syscall<%s>" % self.syscall) def readArgumentValues(self, regs): if RUNNING_BSD: sp = self.process.getStackPointer() return [ self.process.readWord(sp + index*CPU_WORD_SIZE) for index in xrange(1, 6+1) ] if CPU_I386: return (regs.ebx, regs.ecx, regs.edx, regs.esi, regs.edi, regs.ebp) if CPU_X86_64: return (regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9) if CPU_POWERPC: return (regs.gpr3, regs.gpr4, regs.gpr5, regs.gpr6, regs.gpr7, regs.gpr8) raise NotImplementedError() def readArguments(self, argument_values): if self.name in SYSCALL_PROTOTYPES: self.restype, formats = SYSCALL_PROTOTYPES[self.name] for value, format in izip(argument_values, formats): argtype, argname = format self.addArgument(value=value, name=argname, type=argtype) else: for value in argument_values: self.addArgument(value=value) def exit(self): if self.name in PREFORMAT_ARGUMENTS: preformat = set(PREFORMAT_ARGUMENTS[self.name]) else: preformat = set() # Data pointed by arguments may have changed during the syscall # eg. uname() syscall for index, argument in enumerate(self.arguments): if index in preformat: # Don't lose preformatted arguments continue if argument.type and not argument.type.endswith("*"): continue argument.text = None if CPU_I386: regname = "eax" elif CPU_X86_64: regname = "rax" elif CPU_POWERPC: regname = "result" else: raise NotImplementedError() self.result = self.process.getreg(regname) if self.restype.endswith("*"): text = formatAddress(self.result) else: uresult = self.result self.result = ulong2long(self.result) if self.result < 0: text = "%s (%s)" % ( self.result, strerror(-self.result)) elif not(0 <= self.result <= 9): text = "%s (%s)" % (self.result, formatWordHex(uresult)) else: text = str(self.result) self.result_text = text return text def __str__(self): return "" % self.name python-ptrace-0.6.4/ptrace/syscall/socketcall.py0000644000175000017500000000447211527075271022244 0ustar haypohaypo00000000000000from ptrace.cpu_info import CPU_WORD_SIZE from ptrace.ctypes_tools import ntoh_ushort, ntoh_uint from ptrace.syscall import SYSCALL_PROTOTYPES from ptrace.syscall.socketcall_constants import SOCKETCALL, SOCKET_FAMILY from ptrace.syscall.socketcall_struct import sockaddr, sockaddr_in, sockaddr_in6, sockaddr_un from ctypes import c_int from ptrace.os_tools import RUNNING_LINUX from socket import AF_INET, AF_INET6, inet_ntoa from struct import pack if RUNNING_LINUX: from socket import AF_NETLINK from ptrace.syscall.socketcall_struct import sockaddr_nl AF_FILE = 1 def formatOptVal(argument): function = argument.function optlen = function["optlen"].value if optlen == 4: addr = argument.value text = function.process.readStruct(addr, c_int) return argument.formatPointer("<%s>" % text, addr) else: return None def formatSockaddr(argument, argtype): address = argument.value value = argument.function.process.readStruct(address, sockaddr) family = value.family if family == AF_INET: return argument.readStruct(address, sockaddr_in) if family == AF_INET6: return argument.readStruct(address, sockaddr_in6) if family == AF_FILE: return argument.readStruct(address, sockaddr_un) if RUNNING_LINUX: if family == AF_NETLINK: return argument.readStruct(address, sockaddr_nl) family = SOCKET_FAMILY.get(family, family) return argument.formatPointer("" % family, address) def setupSocketCall(function, process, socketcall, address): # Reset function call function.clearArguments() # function.argument_class = SocketCallArgument # Setup new function call function.process = process function.name = socketcall.getText() # Create arguments function.restype, formats = SYSCALL_PROTOTYPES[function.name] for argtype, argname in formats: value = process.readWord(address) function.addArgument(value, argname, argtype) address += CPU_WORD_SIZE def formatSockaddrInStruct(argument, name, value): if name == "sin_port": return ntoh_ushort(value) return None def formatSockaddrIn6Struct(argument, name, value): if name == "sin6_port": return ntoh_ushort(value) #if name == "sin6_addr": # FIXME: ... return None python-ptrace-0.6.4/ptrace/syscall/syscall_argument.py0000644000175000017500000001750511527075271023475 0ustar haypohaypo00000000000000from ptrace.cpu_info import CPU_WORD_SIZE from ptrace.ctypes_tools import uint2int, formatWordHex, formatAddress from ptrace.signames import signalName from ctypes import c_int from ptrace.error import PTRACE_ERRORS, writeError from logging import getLogger, INFO from ptrace.func_arg import FunctionArgument from ptrace.syscall.posix_arg import ( formatMmapProt, formatAccessMode, formatOpenMode, formatCloneFlags) from ptrace.func_call import FunctionCall from ptrace.syscall.socketcall import (setupSocketCall, formatOptVal, formatSockaddr, formatSockaddrInStruct, formatSockaddrIn6Struct) from ptrace.syscall.socketcall_constants import SOCKETCALL import re from ptrace.os_tools import RUNNING_LINUX, RUNNING_FREEBSD from ptrace.syscall import FILENAME_ARGUMENTS if RUNNING_LINUX: from ptrace.syscall.linux_struct import ( timeval, timespec, pollfd, rlimit, new_utsname, user_desc) from ptrace.syscall.linux_constants import SYSCALL_ARG_DICT, FD_SETSIZE elif RUNNING_FREEBSD: from ptrace.syscall.freebsd_constants import SYSCALL_ARG_DICT else: SYSCALL_ARG_DICT = {} KNOWN_STRUCTS = [] if RUNNING_LINUX: KNOWN_STRUCTS.extend((timeval, timespec, pollfd, rlimit, new_utsname, user_desc)) KNOWN_STRUCTS = dict( (struct.__name__, struct) for struct in KNOWN_STRUCTS ) ARGUMENT_CALLBACK = { # Prototype: callback(argument) -> str "access": {"mode": formatAccessMode}, "open": {"mode": formatOpenMode}, "mmap": {"prot": formatMmapProt}, "mmap2": {"prot": formatMmapProt}, "clone": {"flags": formatCloneFlags}, "setsockopt": {"optval": formatOptVal}, } POINTER_CALLBACK = { # Prototype: callback(argument, argtype) -> str "sockaddr": formatSockaddr, } STRUCT_CALLBACK = { # Prototype: callback(argument, attr_name, attr_value) -> str "sockaddr_in": formatSockaddrInStruct, "sockaddr_in6": formatSockaddrIn6Struct, } INTEGER_TYPES = set(( "int", "size_t", "clockid_t", "long", "socklen_t", "pid_t", "uid_t", "gid_t", )) def iterBits(data): for char in data: byte = ord(char) for index in xrange(8): yield ((byte >> index) & 1) == 1 class SyscallArgument(FunctionArgument): def createText(self): value = self.value argtype = self.type name = self.name if not argtype or not name: return formatWordHex(self.value) syscall = self.function.name # Special cases try: return SYSCALL_ARG_DICT[syscall][name][value] except KeyError: pass try: callback = ARGUMENT_CALLBACK[syscall][name] except KeyError: callback = None if callback: return callback(self) if syscall == "execve": if name in ("argv", "envp"): return self.readCStringArray(value) if syscall == "socketcall": if name == "call": try: return SOCKETCALL[value][0] except KeyError: return str(value) if name == "args": func_call = FunctionCall("socketcall", self.options) setupSocketCall(func_call, self.function.process, self.function[0], self.value) text = "<%s>" % func_call.format() return self.formatPointer(text, self.value) if syscall == "write" and name == "buf": fd = self.function[0].value if fd < 3: length = self.function[2].value return self.readString(value, length) if name == "signum": return signalName(value) if name in FILENAME_ARGUMENTS: return self.readCString(value) # Remove "const " prefix if argtype.startswith("const "): argtype = argtype[6:] # Format depending on the type if argtype.endswith("*"): try: text = self.formatValuePointer(argtype[:-1]) if text: return text except PTRACE_ERRORS, err: writeError(getLogger(), err, "Warning: Format %r value error" % self, log_level=INFO) return formatAddress(self.value) # Array like "int[2]" match = re.match("(.*)\[([0-9])+\]", argtype) if match: basetype = match.group(1) count = int(match.group(2)) if basetype == "int": return self.readArray(self.value, c_int, count) # Simple types if argtype in ("unsigned int", "unsigned long", "u32"): return str(self.value) if argtype in INTEGER_TYPES: return str(uint2int(self.value)) # Default formatter: hexadecimal return formatWordHex(self.value) def formatValuePointer(self, argtype): address = self.value if not address: return "NULL" if argtype.startswith("struct "): argtype = argtype[7:] # Try a callback try: callback = POINTER_CALLBACK[argtype] except KeyError: callback = None if callback: return callback(self, argtype) if argtype == "int": pointee = self.function.process.readStruct(address, c_int) return self.formatPointer("<%s>" % pointee, address) if argtype in KNOWN_STRUCTS: struct = KNOWN_STRUCTS[argtype] return self.readStruct(address, struct) if RUNNING_LINUX and argtype == "fd_set": fd_set = self.readBits(address, FD_SETSIZE) return self.formatPointer("" % fd_set, address) syscall = self.function.name if syscall == "rt_sigprocmask" and argtype == "sigset_t": size = self.function["sigsetsize"].value * 8 def formatter(key): key += 1 return signalName(key) fd_set = self.readBits(address, size, format=formatter) return self.formatPointer("" % fd_set, address) return None def readBits(self, address, count, format=str): bytes = self.function.process.readBytes(address, count//8) fd_set = [ format(index) for index, bit in enumerate(iterBits(bytes)) if bit ] return ", ".join(fd_set) def readCString(self, address): if address: max_size = self.options.string_max_length char, truncated = self.function.process.readCString(address, max_size) char = repr(char) if truncated: char += "..." else: char = "NULL" return self.formatPointer(char, address) def readString(self, address, size): if address: max_len = self.options.string_max_length truncated = (max_len < size) size = min(size, max_len) bytes = self.function.process.readBytes(address, size) bytes = repr(bytes) if truncated: bytes += "..." else: bytes = "NULL" return self.formatPointer(bytes, address) def readCStringArray(self, address): if not address: return "NULL" address0 = address max_count = self.options.max_array_count text = [] while True: str_addr = self.function.process.readWord(address) address += CPU_WORD_SIZE text.append(self.readCString(str_addr)) if not str_addr: break if max_count <= len(text): text.append("(... more than %s strings ...)" % max_count) break text = "<(%s)>" % ", ".join(text) return self.formatPointer(text, address0) def formatStructValue(self, struct, name, value): if struct in STRUCT_CALLBACK: callback = STRUCT_CALLBACK[struct] return callback(self, name, value) return None python-ptrace-0.6.4/ptrace/syscall/posix_constants.py0000644000175000017500000000301311234417272023340 0ustar haypohaypo00000000000000from ptrace.syscall.socketcall_constants import ( SOCKET_FAMILY, SOCKET_TYPE, SOCKET_PROTOCOL, SETSOCKOPT_LEVEL, SETSOCKOPT_OPTNAME) SYSCALL_ARG_DICT = { "lseek": { "origin": {0: "SEEK_SET", 1: "SEEK_CUR", 2: "SEEK_END"}, }, "futex": { "op": { 0: "FUTEX_WAIT", 1: "FUTEX_WAKE", 2: "FUTEX_FD", 3: "FUTEX_REQUEUE", 4: "FUTEX_CMP_REQUEUE", 5: "FUTEX_WAKE_OP", 6: "FUTEX_LOCK_PI", 7: "FUTEX_UNLOCK_PI", 8: "FUTEX_TRYLOCK_PI", }, }, "fcntl": { "cmd": { 0: "F_DUPFD", 1: "F_GETFD", 2: "F_SETFD", 3: "F_GETFL", 4: "F_SETFL", 5: "F_GETOWN", 6: "F_SETOWN", 7: "F_GETLK", 8: "F_SETLK", 9: "F_SETLKW", }, }, "ipc": { "call": { 1: "SEMOP", 2: "SEMGET", 3: "SEMCTL", 4: "SEMTIMEDOP", 11: "MSGSND", 12: "MSGRCV", 13: "MSGGET", 14: "MSGCTL", 21: "SHMAT", 22: "SHMDT", 23: "SHMGET", 24: "SHMCTL", }, }, "socket": { "family": SOCKET_FAMILY, "type": SOCKET_TYPE, "protocol": SOCKET_PROTOCOL, }, "getsockopt": { "level": SETSOCKOPT_LEVEL, "optname": SETSOCKOPT_OPTNAME, }, } SYSCALL_ARG_DICT["setsockopt"] = SYSCALL_ARG_DICT["getsockopt"] python-ptrace-0.6.4/ptrace/syscall/linux_constants.py0000644000175000017500000000143711234417272023345 0ustar haypohaypo00000000000000from ptrace.syscall.posix_constants import SYSCALL_ARG_DICT SIGSET_SIZE = 64 FD_SETSIZE = 1024 RLIMIT_RESOURCE = { 0: "RLIMIT_CPU", 1: "RLIMIT_FSIZE", 2: "RLIMIT_DATA", 3: "RLIMIT_STACK", 4: "RLIMIT_CORE", 5: "RLIMIT_RSS", 6: "RLIMIT_NPROC", 7: "RLIMIT_NOFILE", 8: "RLIMIT_MEMLOCK", 9: "RLIMIT_AS", 10: "RLIMIT_LOCKS", 11: "RLIMIT_SIGPENDING", 12: "RLIMIT_MSGQUEUE", 13: "RLIMIT_NICE", 14: "RLIMIT_RTPRIO", 15: "RLIMIT_NLIMITS", } SIGPROCMASK_HOW = {0: "SIG_BLOCK", 1: "SIG_UNBLOCK", 2: "SIG_SETMASK"} SYSCALL_ARG_DICT.update({ "getrlimit": {"resource": RLIMIT_RESOURCE}, "setrlimit": {"resource": RLIMIT_RESOURCE}, "sigprocmask": {"how": SIGPROCMASK_HOW}, "rt_sigprocmask": {"how": SIGPROCMASK_HOW}, }) python-ptrace-0.6.4/ptrace/syscall/names.py0000644000175000017500000000074711347424720021221 0ustar haypohaypo00000000000000from ptrace.cpu_info import CPU_64BITS from ptrace.os_tools import RUNNING_LINUX, RUNNING_FREEBSD if RUNNING_LINUX: if CPU_64BITS: from ptrace.syscall.linux_syscall64 import SYSCALL_NAMES, SOCKET_SYSCALL_NAMES else: from ptrace.syscall.linux_syscall32 import SYSCALL_NAMES, SOCKET_SYSCALL_NAMES elif RUNNING_FREEBSD: from ptrace.syscall.freebsd_syscall import SYSCALL_NAMES, SOCKET_SYSCALL_NAMES else: SYSCALL_NAMES = {} SOCKET_SYSCALL_NAMES = set() python-ptrace-0.6.4/ptrace/syscall/socketcall_struct.py0000644000175000017500000000471011527075271023643 0ustar haypohaypo00000000000000from ctypes import Structure, Union, c_char, c_ushort, c_ubyte, c_uint16, c_uint32 from ptrace.os_tools import RUNNING_BSD, RUNNING_LINUX from socket import inet_ntoa from struct import pack from ptrace.ctypes_tools import ntoh_uint, ntoh_ushort def ip_int2str(ip): """ Convert an IP address (as an interger) to a string. >>> ip_int2str(0x7f000001) '127.0.0.1' """ ip_bytes = pack("!I", ip) return inet_ntoa(ip_bytes) if RUNNING_BSD: sa_family_t = c_ubyte else: sa_family_t = c_ushort class sockaddr(Structure): if RUNNING_BSD: _fields_ = ( ("len", c_ubyte), ("family", sa_family_t), ) else: _fields_ = ( ("family", sa_family_t), ) class in_addr(Structure): _fields_ = ( ("s_addr", c_uint32), ) def __repr__(self): ip = ntoh_uint(self.s_addr) return ip_int2str(ip) class in6_addr(Union): _fields_ = ( ("addr8", c_ubyte * 16), ("addr16", c_uint16 * 8), ("addr32", c_uint32 * 4), ) def __repr__(self): text = ':'.join(("%04x" % ntoh_ushort(part)) for part in self.addr16) return "" % text # INET socket class sockaddr_in(Structure): if RUNNING_BSD: _fields_ = ( ("sin_len", c_ubyte), ("sin_family", sa_family_t), ("sin_port", c_uint16), ("sin_addr", in_addr), ) else: _fields_ = ( ("sin_family", sa_family_t), ("sin_port", c_uint16), ("sin_addr", in_addr), ) class sockaddr_in6(Structure): if RUNNING_BSD: _fields_ = ( ("sin6_len", c_ubyte), ("sin6_family", sa_family_t), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", in6_addr), ) else: _fields_ = ( ("sin6_family", sa_family_t), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", in6_addr), ("sin6_scope_ip", c_uint32), ) # UNIX socket class sockaddr_un(Structure): _fields_ = ( ("sun_family", sa_family_t), ("sun_path", c_char*108), ) # Netlink socket if RUNNING_LINUX: class sockaddr_nl(Structure): _fields_ = ( ("nl_family", sa_family_t), ("nl_pad", c_ushort), ("nl_pid", c_uint32), ("nl_groups", c_uint32), ) python-ptrace-0.6.4/ptrace/syscall/freebsd_syscall.py0000644000175000017500000002156611347424720023264 0ustar haypohaypo00000000000000# FreeBSD kernel syscall list from FreeBSD 7.0RC1 for i386 # # List extracted from: # /usr/include/sys/syscall.h SYSCALL_NAMES = { 0: "syscall", 1: "exit", 2: "fork", 3: "read", 4: "write", 5: "open", 6: "close", 7: "wait4", # 8: old creat 9: "link", 10: "unlink", # 11: obsolete execv 12: "chdir", 13: "fchdir", 14: "mknod", 15: "chmod", 16: "chown", 17: "break", 18: "freebsd4_getfsstat", # 19: old lseek 20: "getpid", 21: "mount", 22: "unmount", 23: "setuid", 24: "getuid", 25: "geteuid", 26: "ptrace", 27: "recvmsg", 28: "sendmsg", 29: "recvfrom", 30: "accept", 31: "getpeername", 32: "getsockname", 33: "access", 34: "chflags", 35: "fchflags", 36: "sync", 37: "kill", # 38: old stat 39: "getppid", # 40: old lstat 41: "dup", 42: "pipe", 43: "getegid", 44: "profil", 45: "ktrace", # 46: old sigaction 47: "getgid", # 48: old sigprocmask 49: "getlogin", 50: "setlogin", 51: "acct", # 52: old sigpending 53: "sigaltstack", 54: "ioctl", 55: "reboot", 56: "revoke", 57: "symlink", 58: "readlink", 59: "execve", 60: "umask", 61: "chroot", # 62: old fstat # 63: old getkerninfo # 64: old getpagesize 65: "msync", 66: "vfork", # 67: obsolete vread # 68: obsolete vwrite 69: "sbrk", 70: "sstk", # 71: old mmap 72: "vadvise", 73: "munmap", 74: "mprotect", 75: "madvise", # 76: obsolete vhangup # 77: obsolete vlimit 78: "mincore", 79: "getgroups", 80: "setgroups", 81: "getpgrp", 82: "setpgid", 83: "setitimer", # 84: old wait 85: "swapon", 86: "getitimer", # 87: old gethostname # 88: old sethostname 89: "getdtablesize", 90: "dup2", 92: "fcntl", 93: "select", 95: "fsync", 96: "setpriority", 97: "socket", 98: "connect", # 99: old accept 100: "getpriority", # 101: old send # 102: old recv # 103: old sigreturn 104: "bind", 105: "setsockopt", 106: "listen", # 107: obsolete vtimes # 108: old sigvec # 109: old sigblock # 110: old sigsetmask # 111: old sigsuspend # 112: old sigstack # 113: old recvmsg # 114: old sendmsg # 115: obsolete vtrace 116: "gettimeofday", 117: "getrusage", 118: "getsockopt", 120: "readv", 121: "writev", 122: "settimeofday", 123: "fchown", 124: "fchmod", # 125: old recvfrom 126: "setreuid", 127: "setregid", 128: "rename", # 129: old truncate # 130: old ftruncate 131: "flock", 132: "mkfifo", 133: "sendto", 134: "shutdown", 135: "socketpair", 136: "mkdir", 137: "rmdir", 138: "utimes", # 139: obsolete 4.2 sigreturn 140: "adjtime", # 141: old getpeername # 142: old gethostid # 143: old sethostid # 144: old getrlimit # 145: old setrlimit # 146: old killpg 147: "setsid", 148: "quotactl", # 149: old quota # 150: old getsockname 155: "nfssvc", # 156: old getdirentries 157: "freebsd4_statfs", 158: "freebsd4_fstatfs", 160: "lgetfh", 161: "getfh", 162: "getdomainname", 163: "setdomainname", 164: "uname", 165: "sysarch", 166: "rtprio", 169: "semsys", 170: "msgsys", 171: "shmsys", 173: "freebsd6_pread", 174: "freebsd6_pwrite", 176: "ntp_adjtime", 181: "setgid", 182: "setegid", 183: "seteuid", 188: "stat", 189: "fstat", 190: "lstat", 191: "pathconf", 192: "fpathconf", 194: "getrlimit", 195: "setrlimit", 196: "getdirentries", 197: "freebsd6_mmap", 198: "__syscall", 199: "freebsd6_lseek", 200: "freebsd6_truncate", 201: "freebsd6_ftruncate", 202: "__sysctl", 203: "mlock", 204: "munlock", 205: "undelete", 206: "futimes", 207: "getpgid", 209: "poll", 220: "__semctl", 221: "semget", 222: "semop", 224: "msgctl", 225: "msgget", 226: "msgsnd", 227: "msgrcv", 228: "shmat", 229: "shmctl", 230: "shmdt", 231: "shmget", 232: "clock_gettime", 233: "clock_settime", 234: "clock_getres", 235: "ktimer_create", 236: "ktimer_delete", 237: "ktimer_settime", 238: "ktimer_gettime", 239: "ktimer_getoverrun", 240: "nanosleep", 248: "ntp_gettime", 250: "minherit", 251: "rfork", 252: "openbsd_poll", 253: "issetugid", 254: "lchown", 255: "aio_read", 256: "aio_write", 257: "lio_listio", 272: "getdents", 274: "lchmod", 275: "netbsd_lchown", 276: "lutimes", 277: "netbsd_msync", 278: "nstat", 279: "nfstat", 280: "nlstat", 289: "preadv", 290: "pwritev", 297: "freebsd4_fhstatfs", 298: "fhopen", 299: "fhstat", 300: "modnext", 301: "modstat", 302: "modfnext", 303: "modfind", 304: "kldload", 305: "kldunload", 306: "kldfind", 307: "kldnext", 308: "kldstat", 309: "kldfirstmod", 310: "getsid", 311: "setresuid", 312: "setresgid", # 313: obsolete signanosleep 314: "aio_return", 315: "aio_suspend", 316: "aio_cancel", 317: "aio_error", 318: "oaio_read", 319: "oaio_write", 320: "olio_listio", 321: "yield", # 322: obsolete thr_sleep # 323: obsolete thr_wakeup 324: "mlockall", 325: "munlockall", 326: "__getcwd", 327: "sched_setparam", 328: "sched_getparam", 329: "sched_setscheduler", 330: "sched_getscheduler", 331: "sched_yield", 332: "sched_get_priority_max", 333: "sched_get_priority_min", 334: "sched_rr_get_interval", 335: "utrace", 336: "freebsd4_sendfile", 337: "kldsym", 338: "jail", 340: "sigprocmask", 341: "sigsuspend", 342: "freebsd4_sigaction", 343: "sigpending", 344: "freebsd4_sigreturn", 345: "sigtimedwait", 346: "sigwaitinfo", 347: "__acl_get_file", 348: "__acl_set_file", 349: "__acl_get_fd", 350: "__acl_set_fd", 351: "__acl_delete_file", 352: "__acl_delete_fd", 353: "__acl_aclcheck_file", 354: "__acl_aclcheck_fd", 355: "extattrctl", 356: "extattr_set_file", 357: "extattr_get_file", 358: "extattr_delete_file", 359: "aio_waitcomplete", 360: "getresuid", 361: "getresgid", 362: "kqueue", 363: "kevent", 371: "extattr_set_fd", 372: "extattr_get_fd", 373: "extattr_delete_fd", 374: "__setugid", 375: "nfsclnt", 376: "eaccess", 378: "nmount", 379: "kse_exit", 380: "kse_wakeup", 381: "kse_create", 382: "kse_thr_interrupt", 383: "kse_release", 384: "__mac_get_proc", 385: "__mac_set_proc", 386: "__mac_get_fd", 387: "__mac_get_file", 388: "__mac_set_fd", 389: "__mac_set_file", 390: "kenv", 391: "lchflags", 392: "uuidgen", 393: "sendfile", 394: "mac_syscall", 395: "getfsstat", 396: "statfs", 397: "fstatfs", 398: "fhstatfs", 400: "ksem_close", 401: "ksem_post", 402: "ksem_wait", 403: "ksem_trywait", 404: "ksem_init", 405: "ksem_open", 406: "ksem_unlink", 407: "ksem_getvalue", 408: "ksem_destroy", 409: "__mac_get_pid", 410: "__mac_get_link", 411: "__mac_set_link", 412: "extattr_set_link", 413: "extattr_get_link", 414: "extattr_delete_link", 415: "__mac_execve", 416: "sigaction", 417: "sigreturn", 421: "getcontext", 422: "setcontext", 423: "swapcontext", 424: "swapoff", 425: "__acl_get_link", 426: "__acl_set_link", 427: "__acl_delete_link", 428: "__acl_aclcheck_link", 429: "sigwait", 430: "thr_create", 431: "thr_exit", 432: "thr_self", 433: "thr_kill", 434: "_umtx_lock", 435: "_umtx_unlock", 436: "jail_attach", 437: "extattr_list_fd", 438: "extattr_list_file", 439: "extattr_list_link", 440: "kse_switchin", 441: "ksem_timedwait", 442: "thr_suspend", 443: "thr_wake", 444: "kldunloadf", 445: "audit", 446: "auditon", 447: "getauid", 448: "setauid", 449: "getaudit", 450: "setaudit", 451: "getaudit_addr", 452: "setaudit_addr", 453: "auditctl", 454: "_umtx_op", 455: "thr_new", 456: "sigqueue", 457: "kmq_open", 458: "kmq_setattr", 459: "kmq_timedreceive", 460: "kmq_timedsend", 461: "kmq_notify", 462: "kmq_unlink", 463: "abort2", 464: "thr_set_name", 465: "aio_fsync", 466: "rtprio_thread", 471: "sctp_peeloff", 472: "sctp_generic_sendmsg", 473: "sctp_generic_sendmsg_iov", 474: "sctp_generic_recvmsg", 475: "pread", 476: "pwrite", 477: "mmap", 478: "lseek", 479: "truncate", 480: "ftruncate", 481: "thr_kill2", } SOCKET_SYSCALL_NAMES = set(( "socket", "socketpair", "connect", "sendto", "recvfrom", "sendmsg", "recvmsg", "bind", "listen", "accept", "getpeername", "getsockname", "getsockopt", "setsockopt", "shutdown", )) python-ptrace-0.6.4/ptrace/ctypes_tools.py0000644000175000017500000000606211527075137021173 0ustar haypohaypo00000000000000from struct import pack, unpack from ptrace.cpu_info import CPU_64BITS from ctypes import cast, POINTER def int2uint64(value): """ Convert a signed 64 bits integer into an unsigned 64 bits integer. """ if value < 0: return 0x10000000000000000 + value else: return value def uint2int64(value): """ Convert an unsigned 64 bits integer into a signed 64 bits integer. """ if value & 0x8000000000000000: return value - 0x10000000000000000 else: return value def truncateWord32(value): """ Truncate an unsigned integer to 32 bits. """ return value & 0xFFFFFFFF def truncateWord64(value): """ Truncate an unsigned integer to 64 bits. """ return value & 0xFFFFFFFFFFFFFFFF def formatUintHex16(value): """ Format an 16 bits unsigned integer. """ return u"0x%04x" % value def formatUintHex32(value): """ Format an 32 bits unsigned integer. """ return u"0x%08x" % value def formatUintHex64(value): """ Format an 64 bits unsigned integer. """ return u"0x%016x" % value def int2uint32(value): """ Convert a signed 32 bits integer into an unsigned 32 bits integer. """ if value < 0: return 0x100000000 + value else: return value def uint2int32(value): """ Convert an unsigned 32 bits integer into a signed 32 bits integer. """ if value & 0x80000000: return value - 0x100000000 else: return value uint2int = uint2int32 int2uint = int2uint32 if CPU_64BITS: ulong2long = uint2int64 long2ulong = int2uint64 formatWordHex = formatUintHex64 truncateWord = truncateWord64 else: ulong2long = uint2int32 long2ulong = int2uint32 formatWordHex = formatUintHex32 truncateWord = truncateWord32 def formatAddress(address): """ Format an address to hexadecimal. Return "NULL" for zero. """ if address: return formatWordHex(address) else: return u"NULL" def formatAddressRange(start, end): """ Format an address range, eg. "0x80004000-0x8000ffff". """ return u"%s-%s" % (formatWordHex(start), formatWordHex(end)) def ntoh_ushort(value): """ Convert an unsigned short integer from network endiant to host endian. """ return unpack("H", value))[0] def ntoh_uint(value): """ Convert an unsigned integer from network endiant to host endian. """ return unpack("I", value))[0] def word2bytes(word): """ Convert an unsigned integer (a CPU word) to a bytes string. """ return pack("L", word) def bytes2word(bytes): """ Convert a bytes string to an unsigned integer (a CPU word). """ return unpack("L", bytes)[0] def bytes2type(bytes, type): """ Cast a bytes string to an objet of the specified type. """ return cast(bytes, POINTER(type))[0] def bytes2array(bytes, basetype, size): """ Cast a bytes string to an array of objets of the specified type and size. """ return bytes2type(bytes, basetype * size) python-ptrace-0.6.4/ptrace/os_tools.py0000644000175000017500000000144511572532406020302 0ustar haypohaypo00000000000000""" Constants about the operating system: - RUNNING_PYPY (bool) - RUNNING_WINDOWS (bool) - RUNNING_LINUX (bool) - RUNNING_FREEBSD (bool) - RUNNING_OPENBSD (bool) - RUNNING_MACOSX (bool) - RUNNING_BSD (bool) - HAS_PROC (bool) - HAS_PTRACE (bool) """ from sys import platform, version, version_info RUNNING_PYTHON3 = version_info[0] == 3 RUNNING_PYPY = ("pypy" in version.lower()) RUNNING_WINDOWS = (platform == 'win32') RUNNING_LINUX = (platform == 'linux2') RUNNING_FREEBSD = (platform.startswith('freebsd') or platform.startswith('gnukfreebsd')) RUNNING_OPENBSD = platform.startswith('openbsd') RUNNING_MACOSX = (platform == 'darwin') RUNNING_BSD = RUNNING_FREEBSD or RUNNING_MACOSX or RUNNING_OPENBSD HAS_PROC = RUNNING_LINUX HAS_PTRACE = (RUNNING_BSD or RUNNING_LINUX) python-ptrace-0.6.4/ptrace/binding/0000775000175000017500000000000011722433414017473 5ustar haypohaypo00000000000000python-ptrace-0.6.4/ptrace/binding/__init__.py0000644000175000017500000000176511234417272021615 0ustar haypohaypo00000000000000from ptrace.binding.func import ( HAS_PTRACE_SINGLESTEP, HAS_PTRACE_EVENTS, HAS_PTRACE_IO, HAS_PTRACE_SIGINFO, HAS_PTRACE_GETREGS, REGISTER_NAMES, ptrace_attach, ptrace_traceme, ptrace_detach, ptrace_kill, ptrace_cont, ptrace_syscall, ptrace_setregs, ptrace_peektext, ptrace_poketext, ptrace_peekuser, ptrace_registers_t) if HAS_PTRACE_EVENTS: from ptrace.binding.func import (WPTRACEEVENT, PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK, PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC, ptrace_setoptions, ptrace_geteventmsg) if HAS_PTRACE_SINGLESTEP: from ptrace.binding.func import ptrace_singlestep if HAS_PTRACE_SIGINFO: from ptrace.binding.func import ptrace_getsiginfo if HAS_PTRACE_IO: from ptrace.binding.func import ptrace_io from ptrace.binding.freebsd_struct import ( ptrace_io_desc, PIOD_READ_D, PIOD_WRITE_D, PIOD_READ_I,PIOD_WRITE_I) if HAS_PTRACE_GETREGS: from ptrace.binding.func import ptrace_getregs python-ptrace-0.6.4/ptrace/binding/openbsd_struct.py0000644000175000017500000000161211234417272023103 0ustar haypohaypo00000000000000from ctypes import Structure, c_int, c_uint, c_ulong, c_void_p, c_char PIOD_READ_D = 1 PIOD_WRITE_D = 2 PIOD_READ_I = 3 PIOD_WRITE_I = 4 size_t = c_ulong pid_t = c_int # /usr/include/machine/reg.h class reg(Structure): _fields_ = ( ("eax", c_uint), ("ecx", c_uint), ("edx", c_uint), ("ebx", c_uint), ("esp", c_uint), ("ebp", c_uint), ("esi", c_uint), ("edi", c_uint), ("eip", c_uint), ("eflags", c_uint), ("cs", c_uint), ("ss", c_uint), ("ds", c_uint), ("es", c_uint), ("fs", c_uint), ("gs", c_uint), ) class fpreg(Structure): _fields_ = ( ("__data", c_char * 116), ) class ptrace_io_desc(Structure): _fields_ = ( ("piod_op", c_int), ("piod_offs", c_void_p), ("piod_addr", c_void_p), ("piod_len", size_t), ) python-ptrace-0.6.4/ptrace/binding/linux_struct.py0000644000175000017500000001266511270326321022614 0ustar haypohaypo00000000000000from ctypes import (Structure, Union, sizeof, c_char, c_ushort, c_int, c_uint, c_ulong, c_void_p, c_uint16, c_uint32, c_uint64) from ptrace.cpu_info import CPU_64BITS, CPU_PPC32 pid_t = c_int uid_t = c_ushort clock_t = c_uint # From /usr/include/asm-i386/user.h class user_regs_struct(Structure): if CPU_PPC32: _fields_ = ( ("gpr0", c_ulong), ("gpr1", c_ulong), ("gpr2", c_ulong), ("gpr3", c_ulong), ("gpr4", c_ulong), ("gpr5", c_ulong), ("gpr6", c_ulong), ("gpr7", c_ulong), ("gpr8", c_ulong), ("gpr9", c_ulong), ("gpr10", c_ulong), ("gpr11", c_ulong), ("gpr12", c_ulong), ("gpr13", c_ulong), ("gpr14", c_ulong), ("gpr15", c_ulong), ("gpr16", c_ulong), ("gpr17", c_ulong), ("gpr18", c_ulong), ("gpr19", c_ulong), ("gpr20", c_ulong), ("gpr21", c_ulong), ("gpr22", c_ulong), ("gpr23", c_ulong), ("gpr24", c_ulong), ("gpr25", c_ulong), ("gpr26", c_ulong), ("gpr27", c_ulong), ("gpr28", c_ulong), ("gpr29", c_ulong), ("gpr30", c_ulong), ("gpr31", c_ulong), ("nip", c_ulong), ("msr", c_ulong), ("orig_gpr3", c_ulong), ("ctr", c_ulong), ("link", c_ulong), ("xer", c_ulong), ("ccr", c_ulong), ("mq", c_ulong), # FIXME: ppc64 => softe ("trap", c_ulong), ("dar", c_ulong), ("dsisr", c_ulong), ("result", c_ulong), ) elif CPU_64BITS: _fields_ = ( ("r15", c_ulong), ("r14", c_ulong), ("r13", c_ulong), ("r12", c_ulong), ("rbp", c_ulong), ("rbx", c_ulong), ("r11", c_ulong), ("r10", c_ulong), ("r9", c_ulong), ("r8", c_ulong), ("rax", c_ulong), ("rcx", c_ulong), ("rdx", c_ulong), ("rsi", c_ulong), ("rdi", c_ulong), ("orig_rax", c_ulong), ("rip", c_ulong), ("cs", c_ulong), ("eflags", c_ulong), ("rsp", c_ulong), ("ss", c_ulong), ("fs_base", c_ulong), ("gs_base", c_ulong), ("ds", c_ulong), ("es", c_ulong), ("fs", c_ulong), ("gs", c_ulong) ) else: _fields_ = ( ("ebx", c_ulong), ("ecx", c_ulong), ("edx", c_ulong), ("esi", c_ulong), ("edi", c_ulong), ("ebp", c_ulong), ("eax", c_ulong), ("ds", c_ushort), ("__ds", c_ushort), ("es", c_ushort), ("__es", c_ushort), ("fs", c_ushort), ("__fs", c_ushort), ("gs", c_ushort), ("__gs", c_ushort), ("orig_eax", c_ulong), ("eip", c_ulong), ("cs", c_ushort), ("__cs", c_ushort), ("eflags", c_ulong), ("esp", c_ulong), ("ss", c_ushort), ("__ss", c_ushort), ) class user_fpregs_struct(Structure): if CPU_64BITS: _fields_ = ( ("cwd", c_uint16), ("swd", c_uint16), ("ftw", c_uint16), ("fop", c_uint16), ("rip", c_uint64), ("rdp", c_uint64), ("mxcsr", c_uint32), ("mxcr_mask", c_uint32), ("st_space", c_uint32 * 32), ("xmm_space", c_uint32 * 64), ("padding", c_uint32 * 24) ) else: _fields_ = ( ("cwd", c_ulong), ("swd", c_ulong), ("twd", c_ulong), ("fip", c_ulong), ("fcs", c_ulong), ("foo", c_ulong), ("fos", c_ulong), ("st_space", c_ulong * 20) ) if not CPU_64BITS: class user_fpxregs_struct(Structure): _fields_ = ( ("cwd", c_ushort), ("swd", c_ushort), ("twd", c_ushort), ("fop", c_ushort), ("fip", c_ulong), ("fcs", c_ulong), ("foo", c_ulong), ("fos", c_ulong), ("mxcsr", c_ulong), ("reserved", c_ulong), ("st_space", c_ulong * 32), ("xmm_space", c_ulong * 32), ("padding", c_ulong * 56) ) # From /usr/include/asm-generic/siginfo.h class _sifields_sigfault_t(Union): _fields_ = ( ("_addr", c_void_p), ) class _sifields_sigchld_t(Structure): _fields_ = ( ("pid", pid_t), ("uid", uid_t), ("status", c_int), ("utime", clock_t), ("stime", clock_t), ) class _sifields_t(Union): _fields_ = ( ("pad", c_char * (128 - 3 * sizeof(c_int))), ("_sigchld", _sifields_sigchld_t), ("_sigfault", _sifields_sigfault_t), # ("_kill", _sifields_kill_t), # ("_timer", _sifields_timer_t), # ("_rt", _sifields_rt_t), # ("_sigpoll", _sifields_sigpoll_t), ) class siginfo(Structure): _fields_ = ( ("si_signo", c_int), ("si_errno", c_int), ("si_code", c_int), ("_sifields", _sifields_t) ) _anonymous_ = ("_sifields",) python-ptrace-0.6.4/ptrace/binding/freebsd_struct.py0000644000175000017500000000156011234417272023065 0ustar haypohaypo00000000000000from ctypes import Structure, c_int, c_uint, c_ulong, c_void_p PIOD_READ_D = 1 PIOD_WRITE_D = 2 PIOD_READ_I = 3 PIOD_WRITE_I = 4 size_t = c_ulong # /usr/include/machine/reg.h class reg(Structure): _fields_ = ( ("fs", c_uint), ("es", c_uint), ("ds", c_uint), ("edi", c_uint), ("esi", c_uint), ("ebp", c_uint), ("isp", c_uint), ("ebx", c_uint), ("edx", c_uint), ("ecx", c_uint), ("eax", c_uint), ("trapno", c_uint), ("err", c_uint), ("eip", c_uint), ("cs", c_uint), ("eflags", c_uint), ("esp", c_uint), ("ss", c_uint), ("gs", c_uint), ) class ptrace_io_desc(Structure): _fields_ = ( ("piod_op", c_int), ("piod_offs", c_void_p), ("piod_addr", c_void_p), ("piod_len", size_t), ) python-ptrace-0.6.4/ptrace/binding/cpu.py0000644000175000017500000000357611275342715020653 0ustar haypohaypo00000000000000from ptrace.cpu_info import CPU_POWERPC, CPU_INTEL, CPU_X86_64, CPU_I386 CPU_INSTR_POINTER = None CPU_STACK_POINTER = None CPU_FRAME_POINTER = None CPU_SUB_REGISTERS = {} if CPU_POWERPC: CPU_INSTR_POINTER = "nip" # FIXME: Is it the right register? CPU_STACK_POINTER = 'gpr1' elif CPU_X86_64: CPU_INSTR_POINTER = "rip" CPU_STACK_POINTER = "rsp" CPU_FRAME_POINTER = "rbp" CPU_SUB_REGISTERS = { # main register name, shift, mask 'al': ('rax', 0, 0xff), 'bl': ('rbx', 0, 0xff), 'cl': ('rcx', 0, 0xff), 'dl': ('rdx', 0, 0xff), 'ah': ('rax', 8, 0xff), 'bh': ('rbx', 8, 0xff), 'ch': ('rcx', 8, 0xff), 'dh': ('rdx', 8, 0xff), 'ax': ('rax', 0, 0xffff), 'bx': ('rbx', 0, 0xffff), 'cx': ('rcx', 0, 0xffff), 'dx': ('rdx', 0, 0xffff), 'eax': ('rax', 32, None), 'ebx': ('rbx', 32, None), 'ecx': ('rcx', 32, None), 'edx': ('rdx', 32, None), } elif CPU_I386: CPU_INSTR_POINTER = "eip" CPU_STACK_POINTER = "esp" CPU_FRAME_POINTER = "ebp" CPU_SUB_REGISTERS = { 'al': ('eax', 0, 0xff), 'bl': ('ebx', 0, 0xff), 'cl': ('ecx', 0, 0xff), 'dl': ('edx', 0, 0xff), 'ah': ('eax', 8, 0xff), 'bh': ('ebx', 8, 0xff), 'ch': ('ecx', 8, 0xff), 'dh': ('edx', 8, 0xff), 'ax': ('eax', 0, 0xffff), 'bx': ('ebx', 0, 0xffff), 'cx': ('ecx', 0, 0xffff), 'dx': ('edx', 0, 0xffff), } if CPU_INTEL: CPU_SUB_REGISTERS.update({ 'cf': ('eflags', 0, 1), 'pf': ('eflags', 2, 1), 'af': ('eflags', 4, 1), 'zf': ('eflags', 6, 1), 'sf': ('eflags', 7, 1), 'tf': ('eflags', 8, 1), 'if': ('eflags', 9, 1), 'df': ('eflags', 10, 1), 'of': ('eflags', 11, 1), 'iopl': ('eflags', 12, 2), }) python-ptrace-0.6.4/ptrace/binding/func.py0000644000175000017500000001756011234417272021011 0ustar haypohaypo00000000000000from os import strerror from ctypes import addressof, c_int from ptrace import PtraceError from ptrace.ctypes_errno import get_errno from ptrace.ctypes_tools import formatAddress from ptrace.os_tools import RUNNING_LINUX, RUNNING_BSD, RUNNING_OPENBSD from ptrace.cpu_info import CPU_64BITS, CPU_WORD_SIZE, CPU_POWERPC if RUNNING_OPENBSD: from ptrace.binding.openbsd_struct import ( reg as ptrace_registers_t, fpreg as user_fpregs_struct) elif RUNNING_BSD: from ptrace.binding.freebsd_struct import ( reg as ptrace_registers_t) elif RUNNING_LINUX: from ptrace.binding.linux_struct import ( user_regs_struct as ptrace_registers_t, user_fpregs_struct, siginfo) if not CPU_64BITS: from ptrace.binding.linux_struct import user_fpxregs_struct else: raise NotImplementedError("Unknown OS!") REGISTER_NAMES = tuple( name for name, type in ptrace_registers_t._fields_ ) HAS_PTRACE_SINGLESTEP = True HAS_PTRACE_EVENTS = False HAS_PTRACE_IO = False HAS_PTRACE_SIGINFO = False HAS_PTRACE_GETREGS = False pid_t = c_int # PTRACE_xxx constants from /usr/include/sys/ptrace.h # (Linux 2.6.21 Ubuntu Feisty i386) PTRACE_TRACEME = 0 PTRACE_PEEKTEXT = 1 PTRACE_PEEKDATA = 2 PTRACE_PEEKUSER = 3 PTRACE_POKETEXT = 4 PTRACE_POKEDATA = 5 PTRACE_POKEUSER = 6 PTRACE_CONT = 7 PTRACE_KILL = 8 if HAS_PTRACE_SINGLESTEP: PTRACE_SINGLESTEP = 9 if RUNNING_OPENBSD: # OpenBSD 4.2 i386 PTRACE_ATTACH = 9 PTRACE_DETACH = 10 HAS_PTRACE_GETREGS = True PTRACE_GETREGS = 33 PTRACE_SETREGS = 34 PTRACE_GETFPREGS = 35 PTRACE_SETFPREGS = 36 HAS_PTRACE_IO = True PTRACE_IO = 11 HAS_PTRACE_SINGLESTEP = True PTRACE_SINGLESTEP = 32 # PT_STEP #HAS_PTRACE_EVENTS = True #PTRACE_SETOPTIONS = 12 # PT_SET_EVENT_MASK #PTRACE_GETEVENTMSG = 14 # PT_GET_PROCESS_STATE elif RUNNING_BSD: # FreeBSD 7.0RC1 i386 PTRACE_ATTACH = 10 PTRACE_DETACH = 11 PTRACE_SYSCALL = 22 if not CPU_POWERPC: HAS_PTRACE_GETREGS = True PTRACE_GETREGS = 33 PTRACE_SETREGS = 34 HAS_PTRACE_IO = True PTRACE_IO = 12 else: # Linux HAS_PTRACE_GETREGS = True PTRACE_GETREGS = 12 PTRACE_SETREGS = 13 PTRACE_ATTACH = 16 PTRACE_DETACH = 17 PTRACE_SYSCALL = 24 if RUNNING_LINUX: PTRACE_GETFPREGS = 14 PTRACE_SETFPREGS = 15 if not CPU_64BITS: PTRACE_GETFPXREGS = 18 PTRACE_SETFPXREGS = 19 HAS_PTRACE_SIGINFO = True PTRACE_GETSIGINFO = 0x4202 PTRACE_SETSIGINFO = 0x4203 HAS_PTRACE_EVENTS = True PTRACE_SETOPTIONS = 0x4200 PTRACE_GETEVENTMSG = 0x4201 PTRACE_O_TRACESYSGOOD = 0x00000001 PTRACE_O_TRACEFORK = 0x00000002 PTRACE_O_TRACEVFORK = 0x00000004 PTRACE_O_TRACECLONE = 0x00000008 PTRACE_O_TRACEEXEC = 0x00000010 PTRACE_O_TRACEVFORKDONE = 0x00000020 PTRACE_O_TRACEEXIT = 0x00000040 # Wait extended result codes for the above trace options PTRACE_EVENT_FORK = 1 PTRACE_EVENT_VFORK = 2 PTRACE_EVENT_CLONE = 3 PTRACE_EVENT_EXEC = 4 PTRACE_EVENT_VFORK_DONE = 5 PTRACE_EVENT_EXIT = 6 try: from cptrace import ptrace as _ptrace HAS_CPTRACE = True except ImportError: HAS_CPTRACE = False from ctypes import c_long, c_ulong from ptrace.ctypes_libc import libc # Load ptrace() function from the system C library _ptrace = libc.ptrace _ptrace.argtypes = (c_ulong, c_ulong, c_ulong, c_ulong) _ptrace.restype = c_ulong def ptrace(command, pid=0, arg1=0, arg2=0, check_errno=False): if HAS_CPTRACE: try: result = _ptrace(command, pid, arg1, arg2) except ValueError, errobj: message = str(errobj) errno = get_errno() raise PtraceError(message, errno=errno, pid=pid) else: result = _ptrace(command, pid, arg1, arg2) result_signed = c_long(result).value if result_signed == -1: errno = get_errno() # peek operations may returns -1 with errno=0: # it's not an error. For other operations, -1 # is always an error if not(check_errno) or errno: message = "ptrace(cmd=%s, pid=%s, %r, %r) error #%s: %s" % ( command, pid, arg1, arg2, errno, strerror(errno)) raise PtraceError(message, errno=errno, pid=pid) return result def ptrace_traceme(): ptrace(PTRACE_TRACEME) def ptrace_attach(pid): ptrace(PTRACE_ATTACH, pid) def ptrace_detach(pid, signal=0): ptrace(PTRACE_DETACH, pid, 0, signal); def _peek(command, pid, address): if address % CPU_WORD_SIZE: raise PtraceError( "ptrace can't read a word from an unaligned address (%s)!" % formatAddress(address), pid=pid) return ptrace(command, pid, address, check_errno=True) def _poke(command, pid, address, word): if address % CPU_WORD_SIZE: raise PtraceError( "ptrace can't write a word to an unaligned address (%s)!" % formatAddress(address), pid=pid) ptrace(command, pid, address, word) def ptrace_peektext(pid, address): return _peek(PTRACE_PEEKTEXT, pid, address) def ptrace_peekdata(pid, address): return _peek(PTRACE_PEEKDATA, pid, address) def ptrace_peekuser(pid, address): return _peek(PTRACE_PEEKUSER, pid, address) def ptrace_poketext(pid, address, word): _poke(PTRACE_POKETEXT, pid, address, word) def ptrace_pokedata(pid, address, word): _poke(PTRACE_POKEDATA, pid, address, word) def ptrace_pokeuser(pid, address, word): _poke(PTRACE_POKEUSER, pid, address, word) def ptrace_kill(pid): ptrace(PTRACE_KILL, pid) if HAS_PTRACE_EVENTS: def WPTRACEEVENT(status): return status >> 16 def ptrace_setoptions(pid, options): ptrace(PTRACE_SETOPTIONS, pid, 0, options) def ptrace_geteventmsg(pid): new_pid = pid_t() ptrace(PTRACE_GETEVENTMSG, pid, 0, addressof(new_pid)) return new_pid.value if RUNNING_LINUX: def ptrace_syscall(pid, signum=0): ptrace(PTRACE_SYSCALL, pid, 0, signum) def ptrace_cont(pid, signum=0): ptrace(PTRACE_CONT, pid, 0, signum) def ptrace_getsiginfo(pid): info = siginfo() ptrace(PTRACE_GETSIGINFO, pid, 0, addressof(info)) return info def ptrace_setsiginfo(pid, info): ptrace(PTRACE_SETSIGINFO, pid, 0, addressof(info)) def ptrace_getfpregs(pid): fpregs = user_fpregs_struct() ptrace(PTRACE_GETFPREGS, pid, 0, addressof(fpregs)) return fpregs def ptrace_setfpregs(pid, fpregs): ptrace(PTRACE_SETFPREGS, pid, 0, addressof(fpregs)) if not CPU_64BITS: def ptrace_getfpxregs(pid): fpxregs = user_fpxregs_struct() ptrace(PTRACE_GETFPXREGS, pid, 0, addressof(fpxregs)) return fpxregs def ptrace_setfpxregs(pid, fpxregs): ptrace(PTRACE_SETFPXREGS, pid, 0, addressof(fpxregs)) if HAS_PTRACE_GETREGS: def ptrace_getregs(pid): regs = ptrace_registers_t() ptrace(PTRACE_GETREGS, pid, 0, addressof(regs)) return regs def ptrace_setregs(pid, regs): ptrace(PTRACE_SETREGS, pid, 0, addressof(regs)) if HAS_PTRACE_SINGLESTEP: def ptrace_singlestep(pid): ptrace(PTRACE_SINGLESTEP, pid) else: def ptrace_syscall(pid, signum=0): ptrace(PTRACE_SYSCALL, pid, 1, signum) def ptrace_cont(pid, signum=0): ptrace(PTRACE_CONT, pid, 1, signum) if HAS_PTRACE_GETREGS: def ptrace_getregs(pid): regs = ptrace_registers_t() ptrace(PTRACE_GETREGS, pid, addressof(regs)) return regs def ptrace_setregs(pid, regs): ptrace(PTRACE_SETREGS, pid, addressof(regs)) if HAS_PTRACE_SINGLESTEP: def ptrace_singlestep(pid): ptrace(PTRACE_SINGLESTEP, pid, 1) if HAS_PTRACE_IO: def ptrace_io(pid, io_desc): ptrace(PTRACE_IO, pid, addressof(io_desc)) python-ptrace-0.6.4/ptrace/version.py0000644000175000017500000000020211722433345020113 0ustar haypohaypo00000000000000PACKAGE = "python-ptrace" VERSION = "0.6.4" WEBSITE = "http://bitbucket.org/haypo/python-ptrace/wiki/Home" LICENSE = "GNU GPL v2" python-ptrace-0.6.4/examples/0000775000175000017500000000000011722433414016421 5ustar haypohaypo00000000000000python-ptrace-0.6.4/examples/itrace.py0000755000175000017500000000335211234417272020250 0ustar haypohaypo00000000000000#!/usr/bin/env python """ Here is a tool which I have been using to debug libc startup code where I didn't find gdb very helpful. It single steps the process and prints each instruction pointer address. To go faster, it allows a number of syscalls to run before starting single-stepping. It's possible to pipe the addresses through addr2line to get a very simple tracing debugger. :-) I couldn't see a way to catch syscalls and single step at the same time. As a consequence the tool can't handle multiple threads. Mark """ import signal from ptrace.debugger import ProcessExit, ProcessSignal import strace class Tracer(strace.SyscallTracer): def createCommonOptions(self, parser): parser.add_option( "-n", dest="syscall_limit", type="int", default=None, help="Number of syscalls before switching to single step") super(Tracer, self).createCommonOptions(parser) def syscallTrace(self, process): syscall_limit = self.options.syscall_limit i = 0 while i < syscall_limit or syscall_limit is None: print i i += 1 process.syscall() self.debugger.waitSyscall() i = 0 while self.debugger: eip = process.getInstrPointer() print i, process.pid, "[%08x]" % eip i += 1 process.singleStep() event = self.debugger.waitProcessEvent() if isinstance(event, ProcessExit): print "process exit" return if (isinstance(event, ProcessSignal) and event.signum & ~128 != signal.SIGTRAP): print "died with signal %i" % event.signum return if __name__ == "__main__": Tracer().main() python-ptrace-0.6.4/examples/simple_dbg.py0000755000175000017500000000314611234417272021107 0ustar haypohaypo00000000000000#!/usr/bin/env python from ptrace.debugger.debugger import PtraceDebugger from ptrace.debugger.child import createChild from ptrace.tools import locateProgram from sys import stderr, argv, exit def playWithProcess(process): # Do anything you want with the process here... print "Dump process registers" process.dumpRegs() print "Continue process execution" process.cont() print "Wait next process event..." event = process.waitEvent() print "New process event: %s" % event def traceProgram(arguments): # Copy the environment variables env = None # Get the full path of the program arguments[0] = locateProgram(arguments[0]) # Create the child process return createChild(arguments, False, env) def main(): # Check the command line if len(argv) < 2: print >>stderr, "usage: %s program [arg1 arg2 ...]" % argv[0] print >>stderr, " or: %s pid" % argv[0] exit(1) # Get the process identifier is_attached = False has_pid = False if len(argv) == 2: try: # User asked to attach a process pid = int(argv[1]) has_pid = True except ValueError: pass if not has_pid: # User asked to create a new program and trace it arguments = argv[1:] pid = traceProgram(arguments) is_attached = True # Create the debugger and attach the process dbg = PtraceDebugger() process = dbg.addProcess(pid, is_attached) # Play with the process and then quit playWithProcess(process) dbg.quit() if __name__ == "__main__": main() python-ptrace-0.6.4/tests/0000775000175000017500000000000011722433414015745 5ustar haypohaypo00000000000000python-ptrace-0.6.4/tests/crash/0000775000175000017500000000000011722433414017045 5ustar haypohaypo00000000000000python-ptrace-0.6.4/tests/crash/div_zero.c0000644000175000017500000000010711234417272021030 0ustar haypohaypo00000000000000int main() { int x = 1; int y = 0; x /= y; return 0; } python-ptrace-0.6.4/tests/crash/socket_ipv4_tcp.c0000644000175000017500000000126311234417272022313 0ustar haypohaypo00000000000000#include #include /* close() */ #include /* perror() */ #include /* struct sockaddr_in */ int main() { int fd; int val; int ret; struct sockaddr_in addr; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) perror("socket"); val = 1; (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr = 0x7f000001; ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr)); if (ret < 0) perror("connect"); ret = close(fd); if (ret) perror("close"); return 0; } python-ptrace-0.6.4/tests/crash/call_null.c0000644000175000017500000000010611234417272021153 0ustar haypohaypo00000000000000int main() { void (*func) (void) = 0; func(); return 0; } python-ptrace-0.6.4/tests/crash/execve.c0000644000175000017500000000054511234417272020474 0ustar haypohaypo00000000000000#include #include #include #include int main() { char *arg[]= {"/bin/ls", NULL }; int pid; pid = fork(); if (pid) { waitpid(pid, NULL, 0); exit(EXIT_SUCCESS); } else { (void)uname(NULL); execve(arg[0], arg, NULL); exit(EXIT_FAILURE); } } python-ptrace-0.6.4/tests/crash/fork.c0000644000175000017500000000201711234417272020152 0ustar haypohaypo00000000000000#include #include #include #include #include #include int parent_pid; void parent(pid_t child_pid) { int status; int result; printf("[parent:%i] wait for child exit\n", parent_pid); result = waitpid(child_pid, &status, 0); if (result != child_pid) { perror("waitpid"); exit(1); } printf("[parent:%i] child exited with status %i\n", parent_pid, status); } void child() { struct utsname buf; int err; int pid = getpid(); printf("[child:%i] uname()\n", pid); err = uname(&buf); if (err) { perror("uname"); exit(1); } printf("[child:%i] done, exit.\n", pid); } int main() { pid_t pid; /* fork process */ parent_pid = getpid(); printf("[parent:%i] fork\n", parent_pid); pid = fork(); if (pid < 0) { perror("fork"); exit(1); } if (pid) { parent(pid); } else { child(); } return 0; } python-ptrace-0.6.4/tests/crash/invalid_read.c0000644000175000017500000000022011234417272021624 0ustar haypohaypo00000000000000typedef struct { int x; int y; int z; } point_t; int main() { point_t *point = 0; int z; z = point->z; return 0; } python-ptrace-0.6.4/tests/crash/abort.c0000644000175000017500000000007611234417272020323 0ustar haypohaypo00000000000000#include int main() { abort(); return 0; } python-ptrace-0.6.4/tests/crash/pthread.c0000644000175000017500000000101311234417272020633 0ustar haypohaypo00000000000000#include #include #define UNUSED(x) x __attribute__((unused)) pthread_t thread; pthread_mutex_t mutex; int global_counter; void* nothing(void *UNUSED(data)) { printf("[thread] exit thread\n"); pthread_exit(NULL); } int main() { global_counter = 1; printf("[main] create thread\n"); pthread_create(&thread, NULL, nothing, NULL); printf("[main] join thread\n"); pthread_join(thread, NULL); printf("[main] exit\n"); pthread_mutex_destroy(&mutex); return 0; } python-ptrace-0.6.4/tests/crash/invalid_write.c0000644000175000017500000000020611234417272022047 0ustar haypohaypo00000000000000typedef struct { int x; int y; int z; } point_t; int main() { point_t *point = 0; point->z = 42; return 0; } python-ptrace-0.6.4/tests/crash/stack_overflow.c0000644000175000017500000000016011234417272022236 0ustar haypohaypo00000000000000void toto() { char buffer[4096]; buffer[0] = 0; toto(); } int main() { toto(); return 0; } python-ptrace-0.6.4/setup_cptrace.py0000755000175000017500000000250311234417272020021 0ustar haypohaypo00000000000000#!/usr/bin/env python SOURCES = ['cptrace/cptrace.c'] CLASSIFIERS = [ 'Intended Audience :: Developers', 'Development Status :: 4 - Beta', 'Environment :: Console', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Operating System :: OS Independent', 'Natural Language :: English', 'Programming Language :: C', 'Programming Language :: Python', ] LONG_DESCRIPTION = open('README.cptrace').read() def main(): from imp import load_source from os import path from sys import argv if "--setuptools" in argv: argv.remove("--setuptools") from setuptools import setup, Extension else: from distutils.core import setup, Extension cptrace_ext = Extension('cptrace', sources=SOURCES) cptrace = load_source("version", path.join("cptrace", "version.py")) install_options = { "name": cptrace.PACKAGE, "version": cptrace.VERSION, "url": cptrace.WEBSITE, "download_url": cptrace.WEBSITE, "license": cptrace.LICENSE, "author": "Victor Stinner", "description": "python binding of ptrace written in C", "long_description": LONG_DESCRIPTION, "classifiers": CLASSIFIERS, "ext_modules": [cptrace_ext], } setup(**install_options) if __name__ == "__main__": main() python-ptrace-0.6.4/AUTHORS0000644000175000017500000000120111572532406015647 0ustar haypohaypo00000000000000python-ptrace authors ===================== Anthony Gelibert - fix cptrace on Apple Dimitris Glynos - follow gdb.py commands Jakub Wilk - fix for GNU/kFreeBSD Mark Seaborn - Create -i option for strace Mickaël Guérin aka KAeL - OpenBSD port teythoon - convert all classes to new-style classes T. Bursztyka aka Tuna - x86_64 port Victor Stinner - python-ptrace author and maintainer Thanks ====== * procps authors (top and uptime programs) python-ptrace-0.6.4/strace.py0000755000175000017500000002310411347424720016442 0ustar haypohaypo00000000000000#!/usr/bin/env python from ptrace import PtraceError from ptrace.debugger import (PtraceDebugger, Application, ProcessExit, ProcessSignal, NewProcessEvent, ProcessExecution) from ptrace.syscall import (SYSCALL_NAMES, SYSCALL_PROTOTYPES, FILENAME_ARGUMENTS, SOCKET_SYSCALL_NAMES) from ptrace.func_call import FunctionCallOptions from sys import stderr, exit from optparse import OptionParser from logging import getLogger, error from ptrace.syscall.socketcall_constants import SOCKETCALL from ptrace.compatibility import any from ptrace.error import PTRACE_ERRORS, writeError from ptrace.ctypes_tools import formatAddress import re class SyscallTracer(Application): def __init__(self): Application.__init__(self) # Parse self.options self.parseOptions() # Setup output (log) self.setupLog() def setupLog(self): if self.options.output: fd = open(self.options.output, 'w') else: fd = stderr self._setupLog(fd) def parseOptions(self): parser = OptionParser(usage="%prog [options] -- program [arg1 arg2 ...]") self.createCommonOptions(parser) parser.add_option("--enter", help="Show system call enter and exit", action="store_true", default=False) parser.add_option("--profiler", help="Use profiler", action="store_true", default=False) parser.add_option("--type", help="Display arguments type and result type (default: no)", action="store_true", default=False) parser.add_option("--name", help="Display argument name (default: no)", action="store_true", default=False) parser.add_option("--string-length", "-s", help="String max length (default: 300)", type="int", default=300) parser.add_option("--array-count", help="Maximum number of array items (default: 20)", type="int", default=20) parser.add_option("--raw-socketcall", help="Raw socketcall form", action="store_true", default=False) parser.add_option("--output", "-o", help="Write output to specified log file", type="str") parser.add_option("--ignore-regex", help="Regex used to filter syscall names (eg. --ignore='^(gettimeofday|futex|f?stat)')", type="str") parser.add_option("--address", help="Display structure addressl", action="store_true", default=False) parser.add_option("--syscalls", '-e', help="Comma separated list of shown system calls (other will be skipped)", type="str", default=None) parser.add_option("--socket", help="Show only socket functions", action="store_true", default=False) parser.add_option("--filename", help="Show only syscall using filename", action="store_true", default=False) parser.add_option("--show-pid", help="Prefix line with process identifier", action="store_true", default=False) parser.add_option("--list-syscalls", help="Display system calls and exit", action="store_true", default=False) parser.add_option("-i", "--show-ip", help="print instruction pointer at time of syscall", action="store_true", default=False) self.createLogOptions(parser) self.options, self.program = parser.parse_args() if self.options.list_syscalls: syscalls = SYSCALL_NAMES.items() syscalls.sort(key=lambda data: data[0]) for num, name in syscalls: print "% 3s: %s" % (num, name) exit(0) if self.options.pid is None and not self.program: parser.print_help() exit(1) # Create "only" filter only = set() if self.options.syscalls: # split by "," and remove spaces for item in self.options.syscalls.split(","): item = item.strip() if not item or item in only: continue ok = True valid_names = SYSCALL_NAMES.values() for name in only: if name not in valid_names: print >>stderr, "ERROR: unknow syscall %r" % name ok = False if not ok: print >>stderr print >>stderr, "Use --list-syscalls options to get system calls list" exit(1) # remove duplicates only.add(item) if self.options.filename: for syscall, format in SYSCALL_PROTOTYPES.iteritems(): restype, arguments = format if any(argname in FILENAME_ARGUMENTS for argtype, argname in arguments): only.add(syscall) if self.options.socket: only |= SOCKET_SYSCALL_NAMES self.only = only if self.options.ignore_regex: try: self.ignore_regex = re.compile(self.options.ignore_regex) except Exception, err: print "Invalid regular expression! %s" % err print "(regex: %r)" % self.options.ignore_regex exit(1) else: self.ignore_regex = None if self.options.fork: self.options.show_pid = True self.processOptions() def ignoreSyscall(self, syscall): name = syscall.name if self.only and (name not in self.only): return True if self.ignore_regex and self.ignore_regex.match(name): return True return False def displaySyscall(self, syscall): name = syscall.name text = syscall.format() if syscall.result is not None: text = "%-40s = %s" % (text, syscall.result_text) prefix = [] if self.options.show_pid: prefix.append("[%s]" % syscall.process.pid) if self.options.show_ip: prefix.append("[%s]" % formatAddress(syscall.instr_pointer)) if prefix: text = ''.join(prefix) + ' ' + text error(text) def syscallTrace(self, process): # First query to break at next syscall self.prepareProcess(process) while True: # No more process? Exit if not self.debugger: break # Wait until next syscall enter try: event = self.debugger.waitSyscall() process = event.process except ProcessExit, event: self.processExited(event) continue except ProcessSignal, event: event.display() process.syscall(event.signum) continue except NewProcessEvent, event: self.newProcess(event) continue except ProcessExecution, event: self.processExecution(event) continue # Process syscall enter or exit self.syscall(process) def syscall(self, process): state = process.syscall_state syscall = state.event(self.syscall_options) if syscall and (syscall.result is not None or self.options.enter): self.displaySyscall(syscall) # Break at next syscall process.syscall() def processExited(self, event): # Display syscall which has not exited state = event.process.syscall_state if (state.next_event == "exit") \ and (not self.options.enter) \ and state.syscall: self.displaySyscall(state.syscall) # Display exit message error("*** %s ***" % event) def prepareProcess(self, process): process.syscall() process.syscall_state.ignore_callback = self.ignoreSyscall def newProcess(self, event): process = event.process error("*** New process %s ***" % process.pid) self.prepareProcess(process) process.parent.syscall() def processExecution(self, event): process = event.process error("*** Process %s execution ***" % process.pid) process.syscall() def runDebugger(self): # Create debugger and traced process self.setupDebugger() process = self.createProcess() if not process: return self.syscall_options = FunctionCallOptions( write_types=self.options.type, write_argname=self.options.name, string_max_length=self.options.string_length, replace_socketcall=not self.options.raw_socketcall, write_address=self.options.address, max_array_count=self.options.array_count, ) self.syscall_options.instr_pointer = self.options.show_ip self.syscallTrace(process) def main(self): if self.options.profiler: from ptrace.profiler import runProfiler runProfiler(getLogger(), self._main) else: self._main() def _main(self): self.debugger = PtraceDebugger() try: self.runDebugger() except ProcessExit, event: self.processExited(event) except PtraceError, err: error("ptrace() error: %s" % err) except KeyboardInterrupt: error("Interrupted.") except PTRACE_ERRORS, err: writeError(getLogger(), err, "Debugger error") self.debugger.quit() def createChild(self, program): pid = Application.createChild(self, program) error("execve(%s, %s, [/* 40 vars */]) = %s" % ( program[0], program, pid)) return pid if __name__ == "__main__": SyscallTracer().main() python-ptrace-0.6.4/gdb.py0000755000175000017500000006715311572532406015732 0ustar haypohaypo00000000000000#!/usr/bin/env python from ptrace import PtraceError from ptrace.debugger import (PtraceDebugger, Application, ProcessExit, NewProcessEvent, ProcessSignal, ProcessExecution, ProcessError) from optparse import OptionParser from os import getpid from sys import stdout, stderr, exit from logging import getLogger, info, warning, error from ptrace.version import VERSION, WEBSITE from ptrace.error import PTRACE_ERRORS, writeError from ptrace.binding import HAS_PTRACE_SINGLESTEP from ptrace.disasm import HAS_DISASSEMBLER from ptrace.ctypes_tools import (truncateWord, formatWordHex, formatAddress, formatAddressRange, word2bytes) from ptrace.process_tools import dumpProcessInfo from ptrace.tools import inverseDict from ptrace.func_call import FunctionCallOptions from ptrace.signames import signalName, SIGNAMES from signal import SIGTRAP, SIGINT from ptrace.terminal import enableEchoMode, terminalWidth from errno import ESRCH from ptrace.cpu_info import CPU_POWERPC from ptrace.debugger import ChildError from ptrace.debugger.memory_mapping import readProcessMappings from ptrace.os_tools import RUNNING_PYTHON3 import re try: # Use readline for better raw_input() import readline except ImportError: pass # Match a register name: $eax, $gp0, $orig_eax REGISTER_REGEX = re.compile(r"\$[a-z]+[a-z0-9_]+") #BYTES_REGEX = re.compile(r"""(?:'([^'\\]*)'|"([^"\\]*)")""") SIGNALS = inverseDict(SIGNAMES) # name -> signum COMMANDS = ( # trace instructions ("cont", "continue execution"), ("step", "execute one instruction (do not enter in a call)"), ("stepi", "execute one instruction (enter the call)"), ("until", "execute code until specified address (until
)"), ("set", "set register value (set =)"), ("sys", "continue execution to next syscall"), ("signal", "send a signal to the process (signal )"), ("signals", "display signals"), # current process info ("regs", "display registers"), ("where", "display true code content (show breakpoints effects on code). eg. 'where $eip', 'where $eip $eip+20'"), ("print", "display a value (print )"), ("hexdump", "dump memory as specified address or address range (hexdump
or hexdump )"), ("where2", "display original code content (don't show effects of breakpoint on code)"), ("stack", "display stack content"), ("backtrace", "dump the backtrace"), ("proc", "display process information"), ("maps", "display memory mappings"), # breakpoints ("break", "set a breakpoint (break
)"), ("breakpoints", "display breakpoints"), ("delete", "delete a breakpoint (delete
)"), # processes ("attach", 'attach a new process (eg. "attach 2390")'), ("proclist", "list of traced processes"), ("switch", "switch active process (switch or switch )"), ("follow", r'''follow a term (eg. "follow '\x12\x14\x27\x13'")'''), ("showfollow", 'show all "followed" terms'), ("resetfollow", 'reset all "followed" terms'), ("xray", 'show addresses of (and possible pointers to) "followed" terms'), # other ("dbginfo", "information about the debugger"), ("quit", "quit debugger"), ("help", "display this help"), ) def formatAscii(data): def asciiChar(byte): if 32 <= byte <= 126: return unichr(byte) else: return '.' if RUNNING_PYTHON3: return u''.join(asciiChar(byte) for byte in data) else: return u''.join(asciiChar(ord(byte)) for byte in data) def formatHexa(data): if RUNNING_PYTHON3: return u' '.join(u"%02x" % byte for byte in data) else: return u' '.join(u"%02x" % ord(byte) for byte in data) # finds possible pointer values in process memory space, # pointing to address def getPointers(process, address): address = word2bytes(address) procmaps = readProcessMappings(process) for pm in procmaps: for found in pm.search(address): yield found class Gdb(Application): def __init__(self): Application.__init__(self) # Parse self.options self.parseOptions() # Setup output (log) self.setupLog() self.last_signal = {} # We assume user wants all possible information self.syscall_options = FunctionCallOptions( write_types=True, write_argname=True, write_address=True, ) # FIXME: Remove self.breaks! self.breaks = dict() self.followterms = [] def setupLog(self): self._setupLog(stdout) def parseOptions(self): parser = OptionParser(usage="%prog [options] -- program [arg1 arg2 ...]") self.createCommonOptions(parser) self.createLogOptions(parser) self.options, self.program = parser.parse_args() if self.options.pid is None and not self.program: parser.print_help() exit(1) self.processOptions() self.show_pid = self.options.fork def _continueProcess(self, process, signum=None): if not signum and process in self.last_signal: signum = self.last_signal[process] if signum: error("Send %s to %s" % (signalName(signum), process)) process.cont(signum) try: del self.last_signal[process] except KeyError: pass else: process.cont() def cont(self, signum=None): for process in self.debugger: process.syscall_state.clear() if process == self.process: self._continueProcess(process, signum) else: self._continueProcess(process) # Wait for a process signal signal = self.debugger.waitSignals() process = signal.process # Hit breakpoint? if signal.signum == SIGTRAP: ip = self.process.getInstrPointer() if not CPU_POWERPC: # Go before "INT 3" instruction ip -= 1 breakpoint = self.process.findBreakpoint(ip) if breakpoint: error("Stopped at %s" % breakpoint) breakpoint.desinstall(set_ip=True) else: self.processSignal(signal) return None def readRegister(self, regs): name = regs.group(0)[1:] value = self.process.getreg(name) return str(value) def parseInteger(self, text): # Remove spaces and convert to lower case text = text.strip() if " " in text: raise ValueError("Space are forbidden: %r" % text) text = text.lower() # Replace registers by their value orig_text = text text = REGISTER_REGEX.sub(self.readRegister, text) # Replace hexadecimal numbers by decimal numbers def readHexadecimal(regs): text = regs.group(0) if text.startswith("0x"): text = text[2:] elif not re.search("[a-f]", text): return text value = int(text, 16) return str(value) text = re.sub(r"(?:0x)?[0-9a-f]+", readHexadecimal, text) # Reject invalid characters if not re.match(r"^[()<>+*/&0-9-]+$", text): raise ValueError("Invalid expression: %r" % orig_text) # Use integer division (a//b) instead of float division (a/b) text = text.replace("/", "//") # Finally, evaluate the expression is_pointer = text.startswith("*") if is_pointer: text = text[1:] try: value = eval(text) value = truncateWord(value) except SyntaxError: raise ValueError("Invalid expression: %r" % orig_text) if is_pointer: value = self.process.readWord(value) return value def parseIntegers(self, text): values = [] for item in text.split(): item = item.strip() value = self.parseInteger(item) values.append(value) return values def parseBytes(self, text): # FIXME: Validate input # if not BYTES_REGEX.match(text): # raise ValueError('Follow text must be enclosed in quotes!') value = eval(text) if not isinstance(value, str): raise TypeError("Input is not a bytes string!") return value def addFollowTerm(self, text): # Allow terms of the form 'string', "string", '\x04', "\x01\x14" term = self.parseBytes(text) self.followterms.append(term) def showFollowTerms(self): print self.followterms def _xray(self): for term in self.followterms: for process in self.debugger: for procmap in readProcessMappings(process): for address in procmap.search(term): yield (process, procmap, address, term) # displays the offsets of all terms found in the process memory mappings # along with possible addresses of pointers pointing to these terms def xray(self): for process, procmap, address, term in self._xray(): pointers = " ".join(formatAddress(ptr_addr) for ptr_addr in getPointers(process, address)) print "term[%s] pid[%i] %s %s pointers: %s" % ( repr(term), process.pid, procmap, formatAddress(address), pointers) def execute(self, command): errmsg = None if command == "cont": errmsg = self.cont() elif command == "proc": self.procInfo() elif command == "proclist": self.procList() elif command.startswith("attach "): errmsg = self.attachProcess(command[7:]) elif command == "regs": self.process.dumpRegs() elif command == "stack": self.process.dumpStack() elif command == "backtrace": errmsg = self.backtrace() elif command == "where" or command.startswith("where "): errmsg = self.where(command[6:]) elif command == "where2" or command.startswith("where2 "): errmsg = self.where(command[7:], manage_bp=True) elif command == "maps": self.process.dumpMaps() elif command == "dbginfo": self.debuggerInfo() elif command == "step": errmsg = self.step(False) elif command == "stepi": errmsg = self.step(True) elif command == "sys": errmsg = self.syscallTrace() elif command == "help": self.help() elif command.startswith("set "): errmsg = self.set(command) elif command.startswith("until "): errmsg = self.until(command[6:]) elif command.startswith("switch") or command == "switch": errmsg = self.switch(command[6:]) elif command.startswith("break "): errmsg = self.breakpoint(command[6:]) elif command.startswith("breakpoints"): self.displayBreakpoints() elif command.startswith("signals"): self.displaySignals() elif command.startswith("delete "): errmsg = self.delete(command[7:]) elif command.startswith("hexdump "): errmsg = self.hexdump(command[8:]) elif command.startswith("signal "): errmsg = self.signal(command[7:]) elif command.startswith("print "): errmsg = self.print_(command[6:]) elif command.startswith("follow "): errmsg = self.addFollowTerm(command[7:]) elif command == "showfollow": self.showFollowTerms() elif command == "resetfollow": self.followterms = [] elif command == "xray": self.xray() else: errmsg = "Unknown command: %r" % command if errmsg: print >>stderr, errmsg return False return True def parseSignum(self, command): try: return SIGNALS[command] except KeyError: pass try: return SIGNALS["SIG"+command] except KeyError: pass try: return self.parseInteger(command) except ValueError, err: raise ValueError("Invalid signal number: %r" % command) def signal(self, command): try: signum = self.parseSignum(command) except ValueError, err: return str(err) last_process = self.process try: errmsg = self.cont(signum) return errmsg finally: try: del self.last_signal[last_process] except KeyError: pass def print_(self, command): try: value = self.parseInteger(command) except ValueError, err: return str(err) error("Decimal: %s" % value) error("Hexadecimal: %s" % formatWordHex(value)) for map in self.process.readMappings(): if value not in map: continue error("Address is part of mapping: %s" % map) return None def hexdump(self, command): max_line = 20 width = (terminalWidth() - len(formatAddress(1)) - 3) // 4 width = max(width, 1) limited = None parts = command.split(" ", 1) if 1 < len(parts): try: start_address = self.parseInteger(parts[0]) end_address = self.parseInteger(parts[1]) if end_address <= start_address: raise ValueError('End address (%s) is smaller than start address(%s)!' % (formatAddress(end_address), formatAddress(start_address))) except ValueError, err: return str(err) size = end_address - start_address max_size = width*max_line if max_size < size: limited = max_size end_address = start_address + max_size else: try: start_address = self.parseInteger(command) except ValueError, err: return str(err) end_address = start_address + 5*width read_error = None address = start_address while address < end_address: size = min(end_address - address, width) try: # Read bytes memory = self.process.readBytes(address, size) # Format bytes hexa = formatHexa(memory) hexa = hexa.ljust(width*3-1, u' ') ascii = formatAscii(memory) ascii = ascii.ljust(width, u' ') # Display previous read error, if any if read_error: warning("Warning: Unable to read memory %s" % ( formatAddressRange(*read_error))) read_error = None # Display line error(u"%s| %s| %s" % (formatAddress(address), hexa, ascii)) except PtraceError: if not read_error: read_error = [address, address + size] else: read_error[1] = address + size address += size # Display last read error, if any if read_error: warning("Warning: Unable to read memory %s" % ( formatAddressRange(*read_error))) if limited: warning("(limit to %s bytes)" % max_size) return None def backtrace(self): trace = self.process.getBacktrace() for func in trace: error(func) if trace.truncated: error("--limited to depth %s--" % len(trace)) return None def where(self, command, manage_bp=False): start = None stop = None try: values = self.parseIntegers(command) except ValueError, err: return str(err) if 1 <= len(values): start = values[0] if 2 <= len(values): stop = values[1] self.process.dumpCode(start, stop, manage_bp=manage_bp) return None def procInfo(self): dumpProcessInfo(error, self.process.pid, max_length=160) def procList(self): for process in self.debugger: text = str(process) if self.process == process: text += " (active)" error(text) def set(self, command): try: key, value = command[4:].split("=", 1) key = key.strip().lower() if not key.startswith("$"): return 'Register name (%s) have to start with "$"' % key key = key[1:] except ValueError, err: return "Invalid command: %r" % command try: value = self.parseInteger(value) except ValueError, err: return str(err) try: self.process.setreg(key, value) except ProcessError, err: return "Unable to set $%s=%s: %s" % (key, value, err) error("Set $%s to %s" % (key, value)) return None def displayInstr(self, prefix): try: if HAS_DISASSEMBLER: instr = self.process.disassembleOne() error("%s %s: %s" % ( prefix, formatAddress(instr.address), instr.text)) else: self.process.dumpCode() except PtraceError, err: error("Unable to read current instruction: %s" % err) def attachProcess(self, text): try: pid = self.parseInteger(text) except ValueError, err: return str(err) process = self.debugger.addProcess(pid, False) self.switchProcess(process) def step(self, enter_call, address=None): if address is None: self.displayInstr("Execute") if (not HAS_PTRACE_SINGLESTEP) or (not enter_call): if address is None: address = self.process.getInstrPointer() size = self.readInstrSize(address, default_size=None) if not size: return "Unable to read instruction size at %s" \ % formatAddress(address) address += size size = self.readInstrSize(address) # Set a breakpoint breakpoint = self.process.createBreakpoint(address, size) # Continue the process self.process.cont() else: # Use ptrace single step command self.process.singleStep() breakpoint = None # Execute processus until next TRAP try: self.process.waitSignals(SIGTRAP) if breakpoint: breakpoint.desinstall(set_ip=True) except: if breakpoint: breakpoint.desinstall() raise return None def newProcess(self, event): error("New process: %s" % event.process) # FIXME: This function doesn't work multiple multiple processes # especially when a parent waits for a child def syscallTrace(self): # Trace until syscall enter self.process.syscall() self.process.waitSyscall() # Process the syscall event state = self.process.syscall_state syscall = state.event(self.syscall_options) # Display syscall if syscall: if syscall.result is not None: text = "%s = %s" % (syscall.format(), syscall.result_text) if self.show_pid: text = "Process %s exits %s" % (syscall.process.pid, text) error(text) else: text = syscall.format() if self.show_pid: text = "Process %s enters %s" % (syscall.process.pid, text) error(text) return None def until(self, command): try: address = self.parseInteger(command) except ValueError, err: return str(err) errmsg = self.step(False, address) if errmsg: return errmsg self.displayInstr("Current") return None def switch(self, command): if not command: process_list = self.debugger.list if len(process_list) == 1: return "There is only one process!" index = process_list.index(self.process) index = (index + 1) % len(process_list) process = process_list[index] self.switchProcess(process) return try: pid = self.parseInteger(command) except ValueError, err: return str(err) try: process = self.debugger[pid] self.switchProcess(process) except KeyError: return "There is not process %s" % pid return None def switchProcess(self, process): if self.process == process: return error("Switch to %s" % process) self.process = process def nextProcess(self): try: process = iter(self.debugger).next() self.switchProcess(process) except StopIteration: pass def displayBreakpoints(self): found = False for process in self.debugger: for bp in process.breakpoints.itervalues(): found = True error("%s:%s" % (process, bp)) if not found: error("(no breakpoint)") def displaySignals(self): signals = SIGNAMES.items() signals.sort(key=lambda (key, value): key) for signum, name in signals: error("% 2s: %s" % (signum, name)) def readInstrSize(self, address, default_size=None): if not HAS_DISASSEMBLER: return default_size try: # Get address and size of instruction at specified address instr = self.process.disassembleOne(address) return instr.size except PtraceError, err: warning("Warning: Unable to read instruction size at %s: %s" % ( formatAddress(address), err)) return default_size def breakpoint(self, command): try: address = self.parseInteger(command) except ValueError, err: return str(err) # Create breakpoint size = self.readInstrSize(address) try: bp = self.process.createBreakpoint(address, size) except PtraceError, err: return "Unable to set breakpoint at %s: %s" % ( formatAddress(address), err) error("New breakpoint: %s" % bp) return None def delete(self, command): try: address = self.parseInteger(command) except ValueError, err: return str(err) breakpoint = self.process.findBreakpoint(address) if not breakpoint: return "No breakpoint at %s " % formatAddress(address) breakpoint.desinstall() error("%s deleted" % breakpoint) return None def help(self): for command, description in COMMANDS: error("%s: %s" % (command, description)) error('') error("Value can be an hexadecimal/decimal number or a register name ($reg)") error("You can use operators a+b, a-b, a*b, a/b, a<>b, a**b, and parenthesis in expressions") error('Use ";" to write multiple commands on the same line (eg. "step; print $eax")') def processSignal(self, event): event.display() self.switchProcess(event.process) self.last_signal[self.process] = event.signum error("%s interrupted by %s" % (self.process, event.name)) def processExecution(self, event): error(event) self.switchProcess(event.process) self.interrupt() def debuggerInfo(self): error("Debugger process ID: %s" % getpid()) error("python-ptrace version %s" % VERSION) error("Website: %s" % WEBSITE) def interrupt(self): waitlist = [] for process in self.debugger: if process.is_stopped: continue try: if process.isTraced(): continue except NotImplementedError: pass warning("Interrupt %s (send SIGINT)" % process) process.kill(SIGINT) waitlist.append(process) for process in waitlist: info("Wait %s interruption" % process) try: process.waitSignals(SIGINT) except ProcessSignal, event: event.display() except KeyboardInterrupt: pass def deleteProcess(self, pid): try: process = self.debugger[pid] except KeyError: return event = process.processTerminated() error(str(event)) if process == self.process: self.nextProcess() def restoreTerminal(self): if enableEchoMode(): error("Terminal: restore echo mode") def mainLoop(self): # Read command try: self.restoreTerminal() command = raw_input(self.invite).strip() except EOFError: print return True except KeyboardInterrupt: error("User interrupt!") self.interrupt() return False # If command is empty, reuse previous command if not command: if self.previous_command: command = self.previous_command info("Replay previous command: %s" % command) else: return False self.previous_command = None # User wants to quit? if command == "quit": return True # Execute the user command try: command_str = command ok = True for command in command_str.split(";"): command = command.strip() try: ok &= self.execute(command) except Exception, err: print "Command error: %s" % err ok = False if not ok: break if ok: self.previous_command = command_str except KeyboardInterrupt: self.interrupt() except NewProcessEvent, event: self.newProcess(event) except ProcessSignal, event: self.processSignal(event) except ProcessExit, event: error(event) self.nextProcess() except ProcessExecution, event: self.processExecution(event) except PtraceError, err: error("ERROR: %s" % err) if err.errno == ESRCH: self.deleteProcess(err.pid) return False def runDebugger(self): self.setupDebugger() # Create new process try: self.process = self.createProcess() except ChildError, err: writeError(getLogger(), err, "Unable to create child process") return if not self.process: return # Trace syscalls self.invite = '(gdb) ' self.previous_command = None while True: if not self.debugger: # There is no more process: quit return done = self.mainLoop() if done: return def main(self): self.debugger = PtraceDebugger() try: self.runDebugger() except KeyboardInterrupt: error("Interrupt debugger: quit!") except PTRACE_ERRORS, err: writeError(getLogger(), err, "Debugger error") self.process = None self.debugger.quit() error("Quit gdb.") self.restoreTerminal() if __name__ == "__main__": Gdb().main()