├── Makefile ├── README.md ├── SConstruct ├── build ├── darwin │ └── SConscript ├── linux2 │ └── SConscript └── win32 │ ├── SConscript │ ├── iconv.dll │ ├── libxml2.dll │ ├── pinpy.dll │ ├── pinpy.dll.manifest │ ├── pinpy.lib │ ├── pinvm.dll │ ├── python27.dll │ └── zlib1.dll ├── examples ├── sample.c ├── sample.py ├── sample.xml └── test-program.c ├── libs └── pinpylibs.py └── src ├── knowledge ├── callgraph.cpp ├── callgraph.h └── knowledge.h ├── main.cpp ├── pin ├── pintool.cpp └── pintool.h ├── pinfunction.cpp ├── pinfunction.h ├── pinfunctionprototype.cpp ├── pinfunctionprototype.h ├── pinpy.cpp ├── pinpy.h ├── pinpyaction.h ├── python ├── pyhelper.cpp └── pyhelper.h ├── utils.cpp └── utils.h /Makefile: -------------------------------------------------------------------------------- 1 | SRCS=src/main.cpp src/pinpy.cpp src/utils.cpp src/pinfunction.cpp src/pinfunctionprototype.cpp 2 | OBJS=$(patsubst %.cpp, %.o, $(SRCS)) 3 | EXEC=pinpy-bin 4 | CC=clang++ 5 | CFLAGS=-g -Wall -O2 -fno-strict-aliasing -std=c++98 -fPIC 6 | OFLAGS=-lxml2 -lpcrecpp -Wl,-all_load 7 | LFLAGS=-L/opt/local/lib/ 8 | INC=-I/opt/local/include/libxml2 -I/opt/local/include -I. 9 | 10 | %.o : %.cpp 11 | $(CC) -c $(CFLAGS) $(INC) $< -o $@ 12 | 13 | $(EXEC) : $(OBJS) 14 | $(CC) -o $(EXEC) $(OFLAGS) $(OBJS) $(LFLAGS) 15 | 16 | .PHONY: clean 17 | clean: 18 | @rm -f $(OBJS) $(EXEC) $(ARNAME) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PinPy - Shake your hooks with some Python! 2 | 3 | Disclaimer: The code is essentially a proof of concept developed by Romain Gaucher ([@rgaucher](https://twitter.com/rgaucher)) and Haris Andrianakis ([@tl0gic](https://twitter.com/tl0gic)) . 4 | It was tested on example programs, and larger. For larger ones, well... it will slow down the execution. 5 | Even if PinPy is not under active development, we decided to release, since the 6 | code might interest few folks out there. 7 | 8 | PinPy essentially allows you to to run Python scripts inside whatever programs, just educate it 9 | using the XML config file, what you want to hook. You'll need to get some information so that 10 | pin can properly install the hooks such as signature, etc. This is done by embedding a Python runtime 11 | in a pin tool. 12 | 13 | Technically, we hook the selected functions using Pin, and redirect the execution flow to the 14 | selected Python function. The XML config allows you to specify when you want to hook the function 15 | call (for instance, before the function, after, etc.). 16 | 17 | # Example 18 | Configuration for Pidgin, we want to intercept log the SSL traffic: 19 | 20 | 21 | 22 | purple_ssl_read 23 | 24 | 25 | gsc 26 | int 27 | 28 | 29 | data 30 | char * 31 | 32 | 33 | len 34 | int 35 | 36 | 37 | int 38 | 39 | 40 | 41 | 42 | 43 | purple_ssl_read -- BeforeCall 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | purple_ssl_read -- AfterCall 53 | 54 | 55 | 56 | 57 | 58 | 59 | In this case, we decide to intercept all calls to `purple_ssl_read_wrapper`, and redirect them 60 | to our logging function `log` implemented in `sample.py`. 61 | 62 | This logging function `log` is called from the Pin tool, and the parameters will be passed to it: 63 | 64 | # ... helpers functions for ctype available in the Python script 65 | # in the example 66 | def log(a_dict): 67 | if a_dict["@callMode"] == "beforeCall": 68 | if a_dict["@funcName"] == "purple_ssl_read": 69 | data = '' 70 | gsc = length = -1 71 | if a_dict.has_key("gsc"): 72 | gsc = readInt(a_dict["gsc"]) 73 | if a_dict.has_key("data"): 74 | data = readStr(a_dict["data"]) 75 | if a_dict.has_key("len"): 76 | length = readInt(a_dict["len"]) 77 | print length, data.size() 78 | # Build & hack on it 79 | Using `scons`, or just tweak the `Makefile`! 80 | 81 | # Links 82 | - [Pin](http://www.pintool.org/), for dynamic instrumentation of binaries 83 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | __normalize_path = lambda x: os.path.abspath(x) 5 | 6 | def good_ext(fext, l=None): 7 | return fext.lower() in l if l else False 8 | 9 | def scandir(directory, files, l_ext, ignore=None): 10 | names = os.listdir(directory) 11 | for name in names: 12 | if ignore and name in ignore: 13 | continue 14 | srcname = __normalize_path(os.path.join(directory, name)) 15 | try: 16 | if os.path.isdir(srcname): 17 | scandir(srcname, files, l_ext, ignore) 18 | elif os.path.isfile(srcname) and good_ext(srcname[srcname.rfind('.') + 1:], l_ext): 19 | if srcname not in files: 20 | files.append(srcname) 21 | except IOError, error: 22 | continue 23 | 24 | def SelectBuildDir(build_dir, platform=None): 25 | # if no platform is specified, then default to sys.platform 26 | if not(platform): 27 | platform = sys.platform 28 | print "Looking for build directory for platform '%s'" % platform 29 | # setup where we start looking at first 30 | test_dir = os.path.join(build_dir, platform) 31 | default_dir = os.path.join(build_dir, 'default') 32 | 33 | # we look for a directory named exactly after the 34 | # platform so that very specific builds can be done 35 | if os.path.exists(test_dir): 36 | # make sure it is a directory 37 | target_dir = test_dir 38 | print "Found directory %s, will build there" % target_dir 39 | return target_dir 40 | 41 | 42 | lib_search_path = ['/lib','/usr/lib','/usr/local/lib'] 43 | include_search_path = ['/usr/include', '/usr/local/include','#src'] 44 | 45 | ## These are our source files 46 | sources = [] 47 | ignored_files = [] 48 | if sys.platform in ('darwin', 'win32'): 49 | ignored_files = ["pythonprobe.cpp"] 50 | 51 | scandir('./src', sources, ["cpp"], ignored_files) 52 | 53 | # Build info 54 | source_base_dir = 'src' 55 | build_base_dir = 'build' 56 | target_name = 'pinpy' 57 | 58 | # Setup some of our requirements 59 | env = Environment() 60 | 61 | # setup the include paths where boost::python and Pin 62 | env.Append(CPPPATH=include_search_path) 63 | env.Append(LIBS=['boost_python', 'python', 'xml2']) 64 | 65 | # variables the sub build directories need 66 | Export('env', 'sources', 'target_name') 67 | 68 | # start the build 69 | target_dir = '#' + SelectBuildDir(build_base_dir) 70 | SConscript(os.path.join(target_dir,'SConscript')) 71 | 72 | env.VariantDir(target_dir, source_base_dir, duplicate=0) 73 | -------------------------------------------------------------------------------- /build/darwin/SConscript: -------------------------------------------------------------------------------- 1 | # import these variables from the parent build script 2 | # this is obivously for testing, pin doesn't work on OSX 3 | Import('env', 'sources', 'target_name') 4 | 5 | BUILD_SHARED = False 6 | 7 | env.Replace(CXX='clang++') 8 | env.Replace(CC='clang++') 9 | env.Replace(CPP='clang++') 10 | env.Replace(LINK='clang++') 11 | 12 | env.Replace(LIBPATH=["/opt/local/lib/", "/opt/local/lib", "/opt/local/Library/Frameworks/Python.framework/Versions/Current/lib"]) 13 | env.Replace(CPPPATH=["/opt/local/include/libxml2", "/opt/local/include", "/opt/local/Library/Frameworks/Python.framework/Versions/Current/include/python2.7", "#src"]) 14 | 15 | env.Replace(CXXFLAGS="-g -Wall -O2 -fno-strict-aliasing -std=c++98 -fPIC") 16 | env.Replace(LINKFLAGS="-g -Wl,-all_load") 17 | 18 | env.Replace(LIBS=["xml2", "boost_python", "python2.7"]) 19 | 20 | if not BUILD_SHARED: 21 | env.Program(target=target_name, source=sources) 22 | else: 23 | env.SharedLibrary(target=target_name, source=sources) -------------------------------------------------------------------------------- /build/linux2/SConscript: -------------------------------------------------------------------------------- 1 | # import these variables from the parent build script 2 | Import('env', 'sources') 3 | # set this to the path where you installed pin 4 | pin_path = "/opt/pin" 5 | 6 | env.Replace(CXX='g++') 7 | env.Replace(CC='g++') 8 | env.Replace(CPP='g++') 9 | env.Replace(LINK='g++') 10 | 11 | env.Replace(LIBPATH=["/usr/lib/",pin_path+"/Lib/",pin_path+"/ExtLib/",pin_path+"/extras/xed2-intel64/lib",pin_path+"/intel64/lib",pin_path+"/intel64/lib-ext"]) 12 | env.Replace(CPPPATH=["#src","#src/pin","#src/python","/usr/include","/usr/include/libxml2","/usr/include/python2.7",pin_path+"/Include",pin_path+"/InstLib",pin_path+"/extras/xed2-intel64/include",pin_path+"/extras/components/include",pin_path+"/source/include",pin_path+"/source/include/gen"]) 13 | 14 | env.Replace(CXXFLAGS="-g -Wall -O3 -fno-strict-aliasing -std=c++98 -Wno-unknown-pragmas -fomit-frame-pointer -DBIGARRAY_MULTIPLIER=1 -DUSING_XED -fno-stack-protector -DTARGET_IA32E -DHOST_IA32E -fPIC -DTARGET_LINUX") 15 | env.Replace(LINKFLAGS="-Wl,--hash-style=sysv -shared -Wl,-Bsymbolic -Wl,--version-script="+pin_path+"/source/include/pintool.ver") 16 | 17 | env.Replace(LIBS=["-lpin","-lxed","-ldwarf","-lelf","-ldl","-lpython2.7","-lboost_python","-lxml2"]) 18 | 19 | env.Program(target="pinpy", source=sources) 20 | env.SharedLibrary(target="pinpy", source=sources) 21 | 22 | Return("env") 23 | -------------------------------------------------------------------------------- /build/win32/SConscript: -------------------------------------------------------------------------------- 1 | # import these variables from the parent build script 2 | Import('env', 'sources', 'target_name') 3 | import os 4 | BUILD_SHARED = True 5 | 6 | env = Environment(ENV = {'PATH' : os.environ['PATH']}) 7 | env['ENV']['TMP'] = os.environ['TMP'] 8 | env.Replace(CXX='cl') 9 | env.Replace(CC='cl') 10 | env.Replace(CPP='cl') 11 | env.Replace(LINK='link') 12 | 13 | # Append lib specifics for windows 14 | env.Replace(LIBPATH=['C:\\Program Files\\boost_1_47\lib', 'C:\\src\\libxml2-2.7.8.win32\\lib', 'C:\\src\\iconv-1.9.2.win32\\lib', 'C:\\src\\pin-2.10\\ia32\\lib', 'C:\\src\\pin-2.10\\ia32\\lib-ext', 'C:\\src\\pin-2.10\\intel64\\lib-ext','C:\\src\\pin-2.10\\extras\\xed2-ia32\\lib', 'C:\\src\\pin-2.10\\extras\\xed2-intel65\\lib', 'C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\lib', 'C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Lib', 'C:\\Python27\\libs']) 15 | 16 | #C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\lib', 'C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0A\\Lib' 17 | 18 | # Include specifics for windows 19 | env.Replace(CPPPATH=['C:\\Python27\\include', 'C:\\Program Files\\boost_1_47', 'C:\\src\\libxml2-2.7.8.win32\\include', 'C:\\src\\iconv-1.9.2.win32\\include', 'C:\\src\\pin-2.10\\source\\include', 'C:\\src\\pin-2.10\\source\\include\\gen', 'C:\\src\\pin-2.10\\source\\tools\\InstLib', 'C:\\src\\pin-2.10\\extras\\xed2-ia32\\include', 'C:\\src\\pin-2.10\\extras\\components\\include', 'C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1\\Include', 'C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\include', '#src']) 20 | 21 | env.Replace(CXXFLAGS=["/MT" ,"/EHs-" ,"/EHa-" ,"/wd4530" ,"/DTARGET_WINDOWS" ,"/DBIGARRAY_MULTIPLIER=1" ,"/DUSING_XED" ,"/DBOOST_PYTHON_STATIC_LIB" ,"/D_CRT_SECURE_NO_DEPRECATE" ,"/D_SECURE_SCL=0" ,"/nologo" ,"/Gy" ,"/O2" , "/DTARGET_IA32" ,"/DHOST_IA32"]) 22 | 23 | if BUILD_SHARED: 24 | env.Replace(LINKFLAGS="/EXPORT:main /NOLOGO /INCREMENTAL:NO /MACHINE:x86 /ENTRY:Ptrace_DllMainCRTStartup@12 /BASE:0x55000000") 25 | env.Replace(LIBS=["pin","libxed", "libcpmt","libcmt","pinvm", "kernel32", "ntdll-32", "iconv", "libxml2", "python27", "libboost_python-vc90-mt-s-1_47"]) 26 | else: 27 | env.Replace(LIBS=["libxml2"]) 28 | 29 | if BUILD_SHARED: 30 | env.SharedLibrary(target=target_name, source=sources) 31 | else: 32 | env.Program(target=target_name, source=sources) 33 | 34 | -------------------------------------------------------------------------------- /build/win32/iconv.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/iconv.dll -------------------------------------------------------------------------------- /build/win32/libxml2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/libxml2.dll -------------------------------------------------------------------------------- /build/win32/pinpy.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/pinpy.dll -------------------------------------------------------------------------------- /build/win32/pinpy.dll.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /build/win32/pinpy.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/pinpy.lib -------------------------------------------------------------------------------- /build/win32/pinvm.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/pinvm.dll -------------------------------------------------------------------------------- /build/win32/python27.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/python27.dll -------------------------------------------------------------------------------- /build/win32/zlib1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuroo/pinpy/65f17876caf35651b22679591a17373e6ddae52d/build/win32/zlib1.dll -------------------------------------------------------------------------------- /examples/sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // gcc -g -O2 sample.c -o sample 7 | int main(int argc, char *argv[]) { 8 | char *buffer = NULL; 9 | size_t length = 0; 10 | 11 | srand(time(0)); 12 | length = rand() % 4096 + 20; 13 | 14 | printf ("Start of the sample application...\n"); 15 | printf ("Allocate %ld\n", length); 16 | 17 | buffer = (char *)malloc(length); 18 | if (buffer) { 19 | strcpy(buffer, "Hello World!\0"); 20 | 21 | printf("Buffer contains: %s\n", buffer); 22 | 23 | printf("Free buffer\n"); 24 | free(buffer); 25 | buffer = NULL; 26 | } 27 | return 0; 28 | } -------------------------------------------------------------------------------- /examples/sample.py: -------------------------------------------------------------------------------- 1 | """ 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | """ 20 | import os, sys 21 | import ctypes 22 | 23 | def writeInt(address, integer): 24 | ''' Write an "integer" at "address" ''' 25 | p = ctypes.c_void_p(address) 26 | i = ctypes.cast(p, ctypes.POINTER(ctypes.c_int)) 27 | i[0] = integer 28 | 29 | def writeStr(address, string): 30 | ''' Write the contents of "string" to the buffer at "address" ''' 31 | p = ctypes.c_void_p(address) 32 | void_ptr = ctypes.cast(p, ctypes.POINTER(ctypes.c_void_p)) 33 | ctypes.memmove(void_ptr.contents.value, string, len(string)) 34 | 35 | def replaceStr(address, string): 36 | ''' Replace the char* pointer at "address" with our string's address ''' 37 | p = ctypes.c_void_p(address) 38 | char_ptr = ctypes.cast(p, ctypes.POINTER(ctypes.c_char_p)) 39 | char_ptr[0] = string 40 | 41 | def readInt(address): 42 | ''' Read an integer from "address" ''' 43 | p = ctypes.c_void_p(address) 44 | i = ctypes.cast(p, ctypes.POINTER(ctypes.c_int)) 45 | return i.contents.value 46 | 47 | def readStr(address): 48 | ''' Read the "string" at "address" ''' 49 | p = ctypes.c_void_p(address) 50 | char_ptr = ctypes.cast(p, ctypes.POINTER(ctypes.c_char_p)) 51 | return char_ptr.contents.value 52 | 53 | def simpleHello(a_dict): 54 | # Handlers for beforeCall 55 | if a_dict["@callMode"] == "beforeCall": 56 | 57 | if a_dict["@funcName"] == "purple_ssl_read": 58 | data = 'unknown' 59 | gsc = length = -1 60 | 61 | if a_dict.has_key("gsc"): 62 | gsc = readInt(a_dict["gsc"]) 63 | 64 | if a_dict.has_key("data"): 65 | data = readStr(a_dict["data"]) 66 | 67 | if a_dict.has_key("len"): 68 | length = readInt(a_dict["len"]) 69 | 70 | print "purple_ssl_read(gsc=" + str(hex(gsc)) + ", data=" + data + ", length=" + str(length) + ")" 71 | 72 | # Handler for wpurple_send beforeCall 73 | if a_dict["@funcName"] == "purple_ssl_write": 74 | data = 'unknown' 75 | gsc = length = -1 76 | 77 | if a_dict.has_key("gsc"): 78 | gsc = readInt(a_dict["gsc"]) 79 | 80 | if a_dict.has_key("data"): 81 | data = readStr(a_dict["data"]) 82 | 83 | if a_dict.has_key("len"): 84 | length = readInt(a_dict["len"]) 85 | 86 | print "purple_ssl_write(gsc=" + str(hex(gsc)) + ", data=" + data + ", length=" + str(length) + ")" 87 | 88 | idx = data.find('') 89 | if idx > 0: 90 | part1 = data[:idx+6] 91 | part2 = data[idx+11:] 92 | newdata = part1+"pwned"+part2 93 | replaceStr(a_dict["data"], newdata) 94 | print "modified purple_ssl_write(gsc=" + str(hex(gsc)) + ", data=" + newdata + ", length=" + str(length) + ")" 95 | 96 | # Handler for sleep beforeCall 97 | if a_dict["@funcName"] == "sleep": 98 | if a_dict.has_key("seconds"): 99 | print "sleep("+str(readInt(a_dict["seconds"]))+")", 100 | else: 101 | print 'python script- error called sleep but arg "seconds" is not set in dict' 102 | 103 | # Handler for myfunction2 beforeCall 104 | # Example usage of replaceStr and writeStr. 105 | # Make sure there is enough space in the buffer you are overwritting 106 | # when using writeStr to hold your new string. Also you cannot (obviously) write 107 | # into constant strings :p 108 | if a_dict["@funcName"] == "myfunction2": 109 | if a_dict.has_key("message"): 110 | print 'beforeCall for myfunction2' 111 | print "message is", readStr(a_dict["message"]) 112 | 113 | new_message = "this is a new message" 114 | replaceStr(a_dict["message"], new_message) 115 | # writeStr(a_dict["message"], new_message) 116 | 117 | # Handlers for afterCall 118 | elif a_dict["@callMode"] == "afterCall": 119 | 120 | # Handler for wpurple_send afterCall 121 | if a_dict["@funcName"] == "purple_ssl_write": 122 | if a_dict.has_key("ret"): 123 | print "purple_ssl_write- ret:", readInt(a_dict["ret"]) 124 | else: 125 | print "- ret: unknown" 126 | 127 | # Handler for sleep afterCall 128 | if a_dict["@funcName"] == "sleep": 129 | if a_dict.has_key("ret"): 130 | print "- retVal:", readInt(a_dict["ret"]) 131 | else: 132 | print 'python script- error we are in afterCall and dont have access to the retVal' 133 | 134 | -------------------------------------------------------------------------------- /examples/sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | purple_ssl_read 10 | 11 | 12 | gsc 13 | int 14 | 15 | 16 | data 17 | char * 18 | 19 | 20 | len 21 | int 22 | 23 | 24 | int 25 | 26 | 27 | 28 | 29 | 30 | 31 | purple_ssl_read -- BeforeCall 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | purple_ssl_read -- AfterCall 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | purple_ssl_write 51 | 52 | 53 | gsc 54 | int 55 | 56 | 57 | data 58 | char * 59 | 60 | 61 | len 62 | int 63 | 64 | 65 | int 66 | 67 | 68 | 69 | 70 | 71 | 72 | purple_ssl_write -- BeforeCall 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | purple_ssl_write -- AfterCall 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /examples/test-program.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int myfunction(int a_int, char a_char, short a_short, long a_long, long long a_long_long) 5 | { 6 | printf("in myfunction\n"); 7 | return 1; 8 | } 9 | 10 | void myfunction2(char *message) 11 | { 12 | printf("c program: the address of message in call is %p\n", message); 13 | printf("c program: message = %s\n", message); 14 | } 15 | 16 | int main(int argc, char **argv) 17 | { 18 | char message[100]; 19 | memset(message, 0, 100); 20 | strncpy(message, "koko lala one two", 99); 21 | 22 | int ret; 23 | ret = sleep(1); 24 | printf("c program: sleep returned %d\n", ret); 25 | printf("c program: message address before call is %p\n", message); 26 | myfunction2(message); 27 | sleep(2); 28 | printf("c program: sleep returned %d\n", ret); 29 | sleep(3); 30 | printf("c program: sleep returned %d\n", ret); 31 | return 0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /libs/pinpylibs.py: -------------------------------------------------------------------------------- 1 | """ 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | """ 20 | import os, sys 21 | 22 | isiterable = lambda obj: isinstance(obj, basestring) or getattr(obj, '__iter__', False) 23 | __normalize_argmt = lambda x: ''.join(x.lower().split()) 24 | __normalize_paths = lambda x: [os.path.abspath(of) for of in x] 25 | __normalize_path = lambda x: os.path.abspath(x) 26 | 27 | # Define several decorators for fast processing, and arguments checking 28 | class Memoized(object): 29 | def __init__(self, func): 30 | self.func = func 31 | self.memoized = {} 32 | self.method_cache = {} 33 | def __call__(self, *args): 34 | return self.cache_get(self.memoized, args, lambda: self.func(*args)) 35 | def __get__(self, obj, objtype): 36 | return self.cache_get(self.method_cache, obj, lambda: self.__class__(functools.partial(self.func, obj))) 37 | def cache_get(self, cache, key, func): 38 | try: 39 | return cache[key] 40 | except KeyError: 41 | cache[key] = func() 42 | return cache[key] 43 | 44 | class NonNullParameters(object): 45 | def __init__(self, func): 46 | self.func = func 47 | def __call__(self, *args): 48 | if isiterable(args): 49 | for e in args: 50 | if e is None: 51 | return None 52 | else: 53 | if args is None: 54 | return None 55 | return self.func(*args) -------------------------------------------------------------------------------- /src/knowledge/callgraph.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "knowledge/callgraph.h" 32 | using namespace boost; 33 | using namespace std; 34 | using namespace knowledge; 35 | 36 | ofstream out("graph-internal.txt"); 37 | 38 | CallGraphNodeId CallGraph::addNode(const CallGraphNode& n) { 39 | CallGraphNodeId nId = getNode(n.address); 40 | if (nId == Graph::null_vertex()) { 41 | CallGraphNodeId nId2 = add_vertex(graph); 42 | // vertex_info[reinterpret_cast(nId2)] = n; 43 | //vertexCache[n.address] = nId2; 44 | out << "Add new vertex: " << nId2 << " address =" << n.address << " (V=" << num_vertices(graph) << ")" << endl; 45 | return nId2; 46 | } 47 | else { 48 | out << "Existing vertex for address = " << " id=" << dec << nId << endl; 49 | } 50 | return nId; 51 | } 52 | 53 | CallGraphNodeId CallGraph::getNode(unsigned int address) { 54 | if (vertexCache.size() < 1) 55 | return Graph::null_vertex(); 56 | 57 | /* 58 | map::iterator cacheIterator = vertexCache.find(address); 59 | if (cacheIterator != vertexCache.end()) 60 | return cacheIterator->second != Graph::null_vertex() ? cacheIterator->second : Graph::null_vertex(); 61 | */ 62 | return Graph::null_vertex(); 63 | } 64 | 65 | bool CallGraph::addEdgeNodeId(const CallGraphNodeId& n1, const CallGraphNodeId& n2) { 66 | if (n1 == Graph::null_vertex() || n2 == Graph::null_vertex()) 67 | return false; 68 | pair edgePair = edge(n1, n2, graph); 69 | if (!edgePair.second) { 70 | // Need to add the edge 71 | pair e = add_edge(n1, n2, graph); 72 | out << "new edge- " << e.first << " (E=" << dec << num_edges(graph) << ")" << flush << endl; 73 | if (e.second) { 74 | //graph[e.first].flow = 1; 75 | } 76 | return e.second; 77 | } 78 | else { 79 | //graph[edgePair.first].flow += 1; 80 | out << "existing edge- " << edgePair.first << " (E=" << dec << num_edges(graph) << ")" << endl; 81 | } 82 | return edgePair.second; 83 | } 84 | 85 | 86 | bool CallGraph::addEdge(unsigned int a1, unsigned int a2) { 87 | // Get the nodes by address, then add an edge 88 | CallGraphNodeId n1 = getNode(a1); 89 | CallGraphNodeId n2 = getNode(a2); 90 | 91 | return addEdgeNodeId(n1, n2); 92 | } 93 | /* 94 | void CallGraph::write(const std::string& fname) { 95 | //boost::dynamic_properties dp; 96 | //dp.property("id", get(name, graph)); 97 | 98 | ofstream out(fname.c_str()); 99 | write_graphviz(out, graph); 100 | 101 | } 102 | */ 103 | 104 | CallGraph::~CallGraph() { 105 | out << "destruct the callgraph" << endl << flush; 106 | out << "############################################" << flush; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/knowledge/callgraph.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef CALLGRAPH_H 21 | #define CALLGRAPH_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace knowledge { 33 | 34 | struct CallGraphNode { 35 | unsigned int address; 36 | std::string name; 37 | 38 | CallGraphNode(unsigned int a=0, const std::string& n="") 39 | : address(a), name(n) 40 | {} 41 | }; 42 | 43 | struct CallGraphEdge { 44 | unsigned int flow; 45 | }; 46 | 47 | typedef boost::adjacency_list Graph; 48 | //typedef boost::adjacency_matrix Graph; 49 | typedef boost::graph_traits::vertex_descriptor CallGraphNodeId; 50 | typedef boost::graph_traits::edge_descriptor CallGraphEdgeId; 51 | typedef boost::graph_traits::vertex_iterator CallGraphNodeIterator; 52 | 53 | // We assume that the vertex_descriptor is an unsigned int, since it should be 54 | // the address in the linked-list. 55 | 56 | class CallGraph { 57 | std::map vertexCache; 58 | std::map vertex_info; 59 | std::map edge_info; 60 | 61 | Graph graph; 62 | 63 | public: 64 | CallGraph() {}; 65 | CallGraph(const CallGraph& c) { 66 | *this = c; 67 | } 68 | CallGraph& operator=(const CallGraph& c) { 69 | vertexCache = c.vertexCache; 70 | vertex_info = c.vertex_info; 71 | edge_info = c.edge_info; 72 | graph = c.graph; 73 | return *this; 74 | } 75 | 76 | CallGraphNodeId getNode(unsigned int address); 77 | CallGraphNodeId addNode(const CallGraphNode& n); 78 | 79 | bool addEdgeNodeId(const CallGraphNodeId&, const CallGraphNodeId&); 80 | bool addEdge(unsigned int, unsigned int); 81 | 82 | unsigned int count_nodes() const { 83 | return num_vertices(graph); 84 | } 85 | 86 | unsigned int count_edges() const { 87 | return num_edges(graph); 88 | } 89 | 90 | //void write(const std::string& fname); 91 | 92 | 93 | 94 | ~CallGraph(); 95 | }; 96 | 97 | 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/knowledge/knowledge.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef KNOWLEDGE_H 21 | #define KNOWLEDGE_H 22 | 23 | #include "knowledge/callgraph.h" 24 | 25 | namespace knowledge { 26 | 27 | struct Knowledge { 28 | 29 | CallGraph callGraph; 30 | //ValueStore valueStore; 31 | 32 | Knowledge(){} 33 | ~Knowledge(){} 34 | 35 | private: 36 | Knowledge(const Knowledge&) {} 37 | Knowledge& operator=(const Knowledge&) { 38 | return *this; 39 | } 40 | }; 41 | 42 | 43 | } 44 | 45 | #endif -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include "Python.h" 21 | #include "pin.H" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #ifdef _MSC_VER 30 | namespace WINDOWS { 31 | #include 32 | } 33 | #else 34 | #include 35 | #endif 36 | 37 | #include "pinpy.h" 38 | 39 | using namespace std; 40 | using namespace pinpy; 41 | 42 | KNOB knobXMLFile(KNOB_MODE_OVERWRITE, "pintool", "i", "", "XML file containing the actions descriptions"); 43 | KNOB knobPythonPath(KNOB_MODE_OVERWRITE, "pintool", "p", "", "Python scripts location (updated python's sys.path)"); 44 | 45 | 46 | void loadPinPy(const map& conf, PinPyModel& model) { 47 | // Load the XML configuration 48 | if (!model.load(conf)) { 49 | cerr << "loadPinPy- Failed to load the XML configuration" << endl; 50 | exit(-1); 51 | } 52 | 53 | map::const_iterator inputConfIterator = conf.find("input"); 54 | 55 | if (inputConfIterator != conf.end()) 56 | cout << "PinPy utility running on " << inputConfIterator->second << endl; 57 | else { 58 | cerr << "loadPinPy- Input is not provided" << endl; 59 | exit(-1); 60 | } 61 | 62 | 63 | map::const_iterator pythonConfIterator = conf.find("python-path"); 64 | 65 | // Prepare python scripts 66 | if (pythonConfIterator != conf.end()) 67 | model.preparePythonScripts(pythonConfIterator->second); 68 | else 69 | model.preparePythonScripts("."); 70 | 71 | if (!model.failed()) { 72 | model.hookFunctions(); 73 | } else { 74 | cerr << "loadPinPy- Failed to load the model" << endl; 75 | exit(-1); 76 | } 77 | 78 | #ifdef DEBUG_PINTOOL 79 | #ifdef _MSC_VER 80 | WINDOWS::Sleep(10 * 1000); 81 | #else 82 | sleep(10); 83 | #endif 84 | #endif 85 | 86 | // Debug by calling one function that calls ctype for sure. 87 | // model.runPython("sample", "debug"); 88 | 89 | 90 | // make symbols available to subsequently loaded .so 91 | #ifdef _MSC_VER 92 | WINDOWS::HMODULE h; 93 | h = WINDOWS::LoadLibraryA("python27.dll"); 94 | #else 95 | void *h; 96 | h = dlopen("/usr/lib/libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); 97 | #endif 98 | 99 | if (!h) { 100 | cerr << "loadPinPy- Failed to load the python library" << endl; 101 | exit(-1); 102 | } 103 | } 104 | 105 | 106 | int Usage() { 107 | cerr << " PinPy is a pintool tool that aims to replace" << endl; 108 | cerr << " any actions (aftercall, beforecall, replace) by " << endl; 109 | cerr << " external python scripts. " << endl; 110 | cerr << endl << KNOB_BASE::StringKnobSummary() << endl; 111 | cerr << endl << " For any questions, please contact " << endl; 112 | return -1; 113 | } 114 | 115 | 116 | int main(int argc, char *argv[]) 117 | { 118 | PinPyModel model; 119 | 120 | // Initialize symbol table code, needed for rtn instrumentation 121 | PIN_InitSymbols(); 122 | 123 | // Initialize pin 124 | if (PIN_Init(argc, argv)) return Usage(); 125 | 126 | map conf; 127 | conf["input"] = knobXMLFile.Value(); 128 | if (knobPythonPath.Value().size() > 0) 129 | conf["python-path"] = knobPythonPath.Value(); 130 | 131 | if (conf.find("input") != conf.end()) 132 | loadPinPy(conf, model); 133 | else { 134 | Usage(); 135 | return -1; 136 | } 137 | 138 | // Start the program, never returns 139 | PIN_StartProgram(); 140 | 141 | return 0; 142 | } 143 | 144 | -------------------------------------------------------------------------------- /src/pin/pintool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include "Python.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "utils.h" 28 | #include "pinpy.h" 29 | #include "pinfunction.h" 30 | #include "pinfunctionprototype.h" 31 | #include "pin/pintool.h" 32 | 33 | using namespace pinpy; 34 | using namespace py; 35 | using namespace std; 36 | using namespace utils; 37 | 38 | namespace pintool { 39 | 40 | 41 | pinpy::PinPyModel* PinHelper::pinPyModelStatic = 0; 42 | py::PyHelper* PinHelper::pyHelperStatic = 0; 43 | 44 | void PinHelper::afterCall(const char *funcName, const PinPyAction* action, vector *pinParams, void *retVal, ...) 45 | { 46 | if (!pyHelperStatic || !action) 47 | return; 48 | 49 | //cerr << funcName << " -> " << action->pyCall.module.c_str() << ", " << action->pyCall.function.c_str() << endl; 50 | 51 | va_list vl; 52 | 53 | PyObject *python_dict = PyDict_New(); 54 | if (!python_dict) { 55 | PyErr_Print(); 56 | return; 57 | } 58 | 59 | PyDict_SetItemString(python_dict, "@threadid", PyInt_FromSize_t((size_t)PIN_GetTid())); 60 | PyDict_SetItemString(python_dict, "@callMode", PyString_FromString("afterCall")); 61 | PyDict_SetItemString(python_dict, "@funcName", PyString_FromString(funcName)); 62 | PyDict_SetItemString(python_dict, "@numArgs", PyInt_FromSize_t(pinParams->size())); 63 | PyDict_SetItemString(python_dict, "@ret", PyInt_FromSize_t((uintptr_t)retVal)); 64 | 65 | va_start(vl, retVal); 66 | for (vector::iterator iter=pinParams->begin(); iter!=pinParams->end(); ++iter) { 67 | PinParameter *cur = &(*iter); 68 | cur->value = va_arg(vl, void*); 69 | PyDict_SetItemString(python_dict, cur->name.c_str(), PyInt_FromSize_t((uintptr_t)cur->value)); 70 | } 71 | va_end(vl); 72 | 73 | pyHelperStatic->run(action->pyCall.module.c_str(), action->pyCall.function.c_str(), python_dict); 74 | Py_DECREF(python_dict); 75 | } 76 | 77 | void PinHelper::beforeCall(const char *funcName, const PinPyAction* action, vector *pinParams, ...) 78 | { 79 | if (!pyHelperStatic || !action) 80 | return; 81 | 82 | //cerr << funcName << " -> " << action->pyCall.module.c_str() << ", " << action->pyCall.function.c_str() << endl; 83 | 84 | va_list vl; 85 | 86 | PyObject *python_dict = PyDict_New(); 87 | if (!python_dict) { 88 | PyErr_Print(); 89 | return; 90 | } 91 | 92 | PyDict_SetItemString(python_dict, "@threadid", PyInt_FromSize_t((size_t)PIN_GetTid())); 93 | PyDict_SetItemString(python_dict, "@callMode", PyString_FromString("beforeCall")); 94 | PyDict_SetItemString(python_dict, "@funcName", PyString_FromString(funcName)); 95 | PyDict_SetItemString(python_dict, "@numArgs", PyInt_FromSize_t(pinParams->size())); 96 | 97 | va_start(vl, pinParams); 98 | for (vector::iterator iter=pinParams->begin(); iter!=pinParams->end(); ++iter) { 99 | PinParameter *cur = &(*iter); 100 | cur->value = va_arg(vl, void*); 101 | PyDict_SetItemString(python_dict, cur->name.c_str(), PyInt_FromSize_t((uintptr_t)cur->value)); 102 | } 103 | va_end(vl); 104 | 105 | pyHelperStatic->run(action->pyCall.module.c_str(), action->pyCall.function.c_str(), python_dict); 106 | Py_DECREF(python_dict); 107 | } 108 | 109 | void PinHelper::setProbe(RTN rtn, IPOINT point, AFUNPTR newfunc, vector *params, ActionType actionType, const ActionList& actionList) 110 | { 111 | const char *funcName = RTN_Name(rtn).c_str(); 112 | unsigned int i = 0; 113 | IARGLIST args = IARGLIST_Alloc(); 114 | 115 | for (;i < params->size(); i++) 116 | IARGLIST_AddArguments(args, IARG_FUNCARG_ENTRYPOINT_REFERENCE, i, IARG_END); 117 | 118 | if (actionType == AFTER_CALL) { 119 | for (ActionList::const_iterator iter=actionList.begin(); iter!=actionList.end(); ++iter) { 120 | const PinPyAction *actionPtr = &(*iter); 121 | RTN_InsertCall(rtn, point, (AFUNPTR)newfunc, IARG_PTR, funcName, IARG_PTR, actionPtr, IARG_PTR, params, IARG_FUNCRET_EXITPOINT_REFERENCE, IARG_IARGLIST, args, IARG_END); 122 | } 123 | } 124 | else { 125 | for (ActionList::const_iterator iter=actionList.begin(); iter!=actionList.end(); ++iter) { 126 | const PinPyAction *actionPtr = &(*iter); 127 | RTN_InsertCall(rtn, point, (AFUNPTR)newfunc, IARG_PTR, funcName, IARG_PTR, actionPtr, IARG_PTR, params, IARG_IARGLIST, args, IARG_END); 128 | } 129 | } 130 | IARGLIST_Free(args); 131 | } 132 | 133 | ofstream out("./graph.txt"); 134 | bool start_tracing = false; 135 | 136 | void PinHelper::processKnowledge(RTN rtn, const PinFunction& function, const PinFunctionPrototype& proto) { 137 | using namespace knowledge; 138 | 139 | 140 | RTN previousRTN = RTN_Prev(rtn); 141 | if (previousRTN != RTN_Invalid()/* && pinPyModelStatic != 0*/) { 142 | const ADDRINT callerAddress = RTN_Address(previousRTN); 143 | const string callerName = RTN_Name(previousRTN); 144 | const string name = RTN_Name(rtn); 145 | const ADDRINT address = RTN_Address(rtn); 146 | 147 | //unsigned int currentThread = PIN_GetTid(); 148 | if (start_tracing) 149 | out << "," << callerName << "," << name << endl << flush; 150 | 151 | /* 152 | CallGraph* callGraph = &(pinPyModelStatic->getKnowledge().callGraph); 153 | 154 | if (callGraph) { 155 | CallGraphNode caller(callerAddress, callerName); 156 | CallGraphNode callee(address, name); 157 | 158 | out << currentThread << " || found: caller at " << hex << callerAddress << " (" << callerName << ")" << endl; 159 | 160 | CallGraphNodeId callerId = callGraph->addNode(caller); 161 | CallGraphNodeId calleeId = callGraph->addNode(callee); 162 | 163 | out << currentThread << " || edge(" << callerId << "," << calleeId << ")" << endl; 164 | 165 | if (false == callGraph->addEdgeNodeId(callerId, calleeId)) { 166 | out << currentThread << " || No idea why I cannot add a node here!" << endl; 167 | out << currentThread << " || ####### " << callerId << " -> " << calleeId << " failed." << endl; 168 | } 169 | out << flush; 170 | } 171 | */ 172 | } 173 | } 174 | 175 | 176 | // Pin calls this function every time a new rtn is executed 177 | VOID PinHelper::routine(RTN rtn, VOID *v) 178 | { 179 | if (!RTN_Valid(rtn)) 180 | return; 181 | 182 | PinFunction& function = *((PinFunction *)v); 183 | const PinFunctionPrototype& proto = function.getPrototype(); 184 | const map& actions = function.getActions(); 185 | const vector& params = proto.getParameters(); 186 | 187 | if (RTN_Name(rtn) == proto.getName()) { 188 | // start_tracing = true; 189 | RTN_Open(rtn); 190 | // cout << "Instrumenting function " << RTN_Name(rtn) << " at address " << hex << RTN_Address(rtn) << endl; 191 | 192 | // TODO: implement REPLACE_CALL 193 | map::const_iterator afterCallIter = actions.find(AFTER_CALL); 194 | map::const_iterator beforeCallIter = actions.find(BEFORE_CALL); 195 | 196 | // Shall we instrument both before/after calls? 197 | if (beforeCallIter != actions.end()) 198 | setProbe(rtn, IPOINT_BEFORE, (AFUNPTR)PinHelper::beforeCall, const_cast *>(¶ms), BEFORE_CALL, beforeCallIter->second); 199 | 200 | if (afterCallIter != actions.end()) 201 | setProbe(rtn, IPOINT_AFTER, (AFUNPTR)PinHelper::afterCall, const_cast *>(¶ms), AFTER_CALL, afterCallIter->second); 202 | 203 | RTN_Close(rtn); 204 | } 205 | 206 | // Process knowledge from current RTN 207 | // processKnowledge(rtn, function, proto); 208 | } 209 | 210 | // This function is called when the application exits 211 | VOID PinHelper::fini(INT32 code, VOID *v) 212 | { 213 | return; 214 | } 215 | 216 | } -------------------------------------------------------------------------------- /src/pin/pintool.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef _PINTOOLH 21 | #define _PINTOOLH 22 | 23 | #include 24 | #include 25 | 26 | #include "knowledge/callgraph.h" 27 | #include "pinfunctionprototype.h" 28 | #include "pinpyaction.h" 29 | #include "python/pyhelper.h" 30 | 31 | // forward definition of PinPyModel 32 | namespace pinpy { 33 | class PinPyModel; 34 | } 35 | 36 | namespace pintool { 37 | 38 | class PinHelper { 39 | 40 | PinHelper(const PinHelper&) {} 41 | PinHelper& operator=(const PinHelper& ) { 42 | return *this; 43 | } 44 | public: 45 | 46 | static pinpy::PinPyModel* PinHelper::pinPyModelStatic; 47 | static py::PyHelper* PinHelper::pyHelperStatic; 48 | 49 | PinHelper() {} 50 | 51 | static void setPinPyModel(pinpy::PinPyModel* m) { 52 | pinPyModelStatic = m; 53 | } 54 | 55 | static void setPyHelper(py::PyHelper* p) { 56 | pyHelperStatic = p; 57 | } 58 | // Fanning place to process any type of analysis 59 | static void processKnowledge(RTN, const pinpy::PinFunction&, const pinpy::PinFunctionPrototype&); 60 | 61 | static void afterCall(const char *funcName, const pinpy::PinPyAction* action, std::vector *pinParams, void *retVal, ...); 62 | static void beforeCall(const char *funcName, const pinpy::PinPyAction* action,std::vector *pinParams, ...); 63 | 64 | static void setProbe(RTN rtn, IPOINT point, AFUNPTR newfunc, std::vector *params, pinpy::ActionType, const pinpy::ActionList&); 65 | 66 | // Pin calls this function every time a new rtn is executed 67 | static VOID routine(RTN rtn, VOID *v); 68 | 69 | // This function is called when the application exits 70 | // It prints the name and count for each procedure 71 | static VOID fini(INT32 code, VOID *v); 72 | 73 | ~PinHelper(){} 74 | }; 75 | 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/pinfunction.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "pinfunction.h" 26 | using namespace std; 27 | using namespace pinpy; 28 | 29 | 30 | ActionType getActionTypeFromString(const string& cstr) { 31 | static map KNOL_ACTION_TYPE; 32 | if (0 == KNOL_ACTION_TYPE.size()) { 33 | KNOL_ACTION_TYPE["beforecall"] = BEFORE_CALL; 34 | KNOL_ACTION_TYPE["aftercall"] = AFTER_CALL; 35 | KNOL_ACTION_TYPE["replacecall"] = REPLACE_CALL; 36 | } 37 | string cstr_ncst(cstr); 38 | transform(cstr_ncst.begin(), cstr_ncst.end(), cstr_ncst.begin(), ::tolower); 39 | 40 | if (KNOL_ACTION_TYPE.find(cstr_ncst) != KNOL_ACTION_TYPE.end()) 41 | return KNOL_ACTION_TYPE[cstr_ncst]; 42 | 43 | // Default: BEFORE_CALL 44 | return NIL_ACTION; 45 | } 46 | 47 | 48 | void PinFunction::setAction(ActionType actionType, const PinPyAction& action) { 49 | if (actions.find(actionType) == actions.end()) 50 | actions[actionType] = ActionList(); 51 | 52 | // TOOD: Need to enforce the order, later on 53 | actions[actionType].push_back(action); 54 | } 55 | 56 | 57 | void PinFunction::setAction(const string& actionTypeStr, const PinPyAction& action) { 58 | ActionType actionType = getActionTypeFromString(actionTypeStr); 59 | if (NIL_ACTION != actionType) 60 | setAction(actionType, action); 61 | } 62 | -------------------------------------------------------------------------------- /src/pinfunction.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef PINFUNCTION_H 21 | #define PINFUNCTION_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "pinfunctionprototype.h" 28 | #include "pinpyaction.h" 29 | 30 | namespace pinpy { 31 | 32 | typedef std::vector ActionList; 33 | 34 | // The PinFunction contains the mapping between 35 | // one function prototype and the several actions 36 | // applied to it. 37 | class PinFunction { 38 | std::string name; 39 | 40 | // Storage of the prototype for this function 41 | PinFunctionPrototype proto; 42 | 43 | // Contains the list of actions, ordered. 44 | // e.g., BEFORE_CALL -> [Action_1, Action_2, etc.] 45 | std::map actions; 46 | 47 | public: 48 | PinFunction() {} 49 | PinFunction(const PinFunction& p) { 50 | *this = p; 51 | } 52 | PinFunction& operator=(const PinFunction& p) { 53 | name = p.name; 54 | proto = p.proto; 55 | actions = p.actions; 56 | return *this; 57 | } 58 | 59 | void setPrototype(const PinFunctionPrototype& p) { 60 | proto = p; 61 | } 62 | 63 | inline const PinFunctionPrototype& getPrototype() { 64 | return proto; 65 | } 66 | 67 | inline std::map& getActions() { 68 | return actions; 69 | } 70 | 71 | void setAction(ActionType, const PinPyAction&); 72 | void setAction(const std::string&, const PinPyAction&); 73 | 74 | inline void setName(const std::string& str) { 75 | name = str; 76 | } 77 | 78 | inline std::string getName() const { 79 | return name; 80 | } 81 | 82 | ~PinFunction() {} 83 | }; 84 | 85 | 86 | } 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/pinfunctionprototype.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include 21 | #include 22 | 23 | #include "pinfunctionprototype.h" 24 | #include "utils.h" 25 | using namespace std; 26 | using namespace pinpy; 27 | using namespace utils; 28 | 29 | // TODO: Need to implement the reverse of the updateParameterType method here 30 | // for now bah. 31 | PinParameter::operator string() const { 32 | string ret = ""; 33 | return ret; 34 | } 35 | 36 | // TODO: This function requires unit testing! 37 | void PinParameter::updateParameterType(const string& typeStr_cst) { 38 | // Stupid normalization happens here 39 | string typeStr(typeStr_cst); 40 | replace(typeStr, " ", " "); 41 | replace(typeStr, "* *", "**"); 42 | 43 | // Let's resume with normal stuff. 44 | vector typeTokens = split(typeStr, " "); 45 | 46 | if (typeTokens.size() < 1) 47 | return; 48 | 49 | // There is a huge normalization process to be done here... 50 | // TODO: Geez, just think about "MyClass * const" vs. "const MyClass *" 51 | // or references to pointers.. "MyClass*&" even though that's stupid shit 52 | // or the "long long", or "long double" ... the split by space is 53 | // just not workking properly. 54 | // Embedding proper parser anyone? 55 | for (vector::const_iterator iter=typeTokens.begin(); iter!=typeTokens.end(); ++iter) { 56 | const string& cur = *iter; 57 | 58 | if ("const" == cur) 59 | isConst = true; 60 | else if ("signed" == cur || "unsigned" == cur) 61 | rangeDecorator = ("signed" == cur ? D_SIGNED : D_UNSIGNED); 62 | else if ("**" == cur || "*" == cur || "&" == cur) 63 | typeDecorator = ("*" == cur ? D_POINTER : ("**" == cur ? D_POINTER_POINTER : D_REFERENCE)); 64 | else { 65 | if ("int" == cur) type = T_INT; 66 | else if ("void" == cur) type = T_VOID; 67 | else if ("char" == cur) type = T_CHAR; 68 | else if ("short" == cur) type = T_SHORT; 69 | else if ("long" == cur) type = T_LONG; 70 | else if ("float" == cur) type = T_FLOAT; 71 | else if ("double" == cur) type = T_DOUBLE; 72 | } 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/pinfunctionprototype.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef PINFUNCTIONPROTOTYPE_H 21 | #define PINFUNCTIONPROTOTYPE_H 22 | 23 | #include 24 | #include 25 | 26 | namespace pinpy { 27 | 28 | // Type used: if structure or class, please use VOID 29 | enum PinParameterType { 30 | T_VOID, 31 | T_CHAR, 32 | T_SHORT, 33 | T_INT, 34 | T_LONG, 35 | T_LONG_LONG, 36 | T_FLOAT, 37 | T_DOUBLE, 38 | T_LONG_DOUBLE, 39 | T_STRING 40 | }; 41 | 42 | // One of the decorators 43 | enum PinParameterDecorator { 44 | D_SIGNED, 45 | D_UNSIGNED, 46 | D_POINTER, 47 | D_POINTER_POINTER, 48 | D_REFERENCE 49 | }; 50 | 51 | struct PinParameter { 52 | unsigned short id; 53 | bool isConst; 54 | PinParameterType type; 55 | // rangeDecorator: unsigned/signed 56 | PinParameterDecorator rangeDecorator; 57 | // typeDecorator: pointer/reference 58 | PinParameterDecorator typeDecorator; 59 | std::string name; 60 | void *value; 61 | 62 | // Update the content of the structure based on the 63 | // supplied string 64 | void updateParameterType(const std::string&); 65 | 66 | // "Serialize" the parameter as a string... More useful than simple 67 | // operator<< overload 68 | operator std::string() const; 69 | }; 70 | 71 | 72 | // PinFunction contains the different parameters that describe 73 | // a function definition. 74 | // The goal is to handle a function prototype at this level. 75 | class PinFunctionPrototype { 76 | // Proto information 77 | unsigned int address; 78 | std::string name; 79 | std::vector parameters; 80 | PinParameter returnType; 81 | 82 | public: 83 | PinFunctionPrototype() {} 84 | 85 | PinFunctionPrototype(const PinFunctionPrototype& p) { 86 | *this = p; 87 | } 88 | 89 | PinFunctionPrototype& operator=(const PinFunctionPrototype& p) { 90 | address = p.address; 91 | name = p.name; 92 | parameters = p.parameters; 93 | returnType = p.returnType; 94 | return *this; 95 | } 96 | 97 | inline std::string getName() const { 98 | return name; 99 | } 100 | 101 | inline const std::vector& getParameters() const { 102 | return parameters; 103 | } 104 | 105 | inline void setName(const std::string& str) { 106 | name = str; 107 | } 108 | 109 | inline void setAddress(const unsigned int addr) { 110 | address = addr; 111 | } 112 | 113 | inline void setFunctionParameters(const std::vector& p) { 114 | parameters = p; 115 | } 116 | 117 | inline void setReturnType(const PinParameter& p) { 118 | returnType = p; 119 | } 120 | 121 | 122 | ~PinFunctionPrototype() {} 123 | }; 124 | 125 | 126 | 127 | 128 | 129 | 130 | } 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /src/pinpy.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include "Python.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "pinpy.h" 31 | #include "utils.h" 32 | #include "pin/pintool.h" 33 | 34 | using namespace std; 35 | using namespace pinpy; 36 | 37 | string getContent(xmlNode *a_node) { 38 | xmlChar *c = xmlNodeGetContent(a_node); 39 | string ret((char *)c); 40 | utils::replace(ret, "\n", ""); 41 | utils::replace(ret, "\r", ""); 42 | utils::replace(ret, "\b", ""); 43 | xmlFree(c); 44 | return ret; 45 | } 46 | 47 | string getChildName(xmlNode *node) { 48 | string content; 49 | if (XML_ELEMENT_NODE == node->type) 50 | content = (char *)node->name; 51 | else if (XML_TEXT_NODE == node->type) 52 | content = getContent(node); 53 | return content; 54 | } 55 | 56 | map getAttributes(xmlNode *node) { 57 | if (!node || !node->properties) 58 | return map(); 59 | xmlAttr *curAttr = 0; 60 | string name, value; 61 | map attributes; 62 | 63 | for (curAttr = node->properties; curAttr; curAttr = curAttr->next) { 64 | if (XML_ATTRIBUTE_NODE == curAttr->type) { 65 | name = (char *)curAttr->name; 66 | if (XML_TEXT_NODE == curAttr->children->type) 67 | value = getChildName(curAttr->children); 68 | if (!(name.empty() || value.empty())) { 69 | attributes[name] = value; 70 | } 71 | name.clear(); 72 | value.clear(); 73 | } 74 | } 75 | return attributes; 76 | } 77 | 78 | 79 | PinPyAction PinPyModel::processPinPyAction(xmlNode *node) { 80 | PinPyAction action; 81 | xmlNode *curNode = node, *iterNode = 0; 82 | string name, value; 83 | 84 | if (!curNode) 85 | return action; 86 | 87 | map pinParameterAttrs = getAttributes(curNode); 88 | 89 | for (iterNode = curNode->children; iterNode; iterNode = iterNode->next) { 90 | if (XML_ELEMENT_NODE == iterNode->type) { 91 | name = (char *)iterNode->name; 92 | 93 | // TODO: We assume only one PyCall per PyAction 94 | // but we might combine later... 95 | if ("PyCall" == name) { 96 | map pyCallAttrs = getAttributes(iterNode); 97 | 98 | action.pyCall.module = pyCallAttrs["module"]; 99 | action.pyCall.function = pyCallAttrs["function"]; 100 | if (iterNode->children) 101 | action.pyCall.message = getContent(iterNode->children); 102 | } 103 | name.clear(); 104 | value.clear(); 105 | } 106 | } 107 | 108 | // Update the action with the proper attributes 109 | action.id = utils::sot(pinParameterAttrs["sequence"]); 110 | action.name = pinParameterAttrs["name"]; 111 | 112 | cerr << action.id << ") Action::name- " << action.name << endl; 113 | cerr << "python " << action.pyCall.module << ":" << action.pyCall.function << " <- " << action.pyCall.message << endl; 114 | return action; 115 | } 116 | 117 | PinParameter PinPyModel::processReturnType(xmlNode *node) { 118 | PinParameter ret; 119 | if (node->children) 120 | ret.updateParameterType(getContent(node->children)); 121 | return ret; 122 | } 123 | 124 | PinParameter PinPyModel::processFunctionParameter(xmlNode *node) { 125 | PinParameter param; 126 | xmlNode *curNode = 0; 127 | string name, value; 128 | 129 | map pinParameterAttrs = getAttributes(node); 130 | 131 | if (!node->children) 132 | return param; 133 | 134 | for(curNode = node->children; curNode; curNode = curNode->next) { 135 | if (XML_ELEMENT_NODE == curNode->type) { 136 | name = (char *)curNode->name; 137 | if ("FunctionParameterName" == name) { 138 | // This field if optional, but useful for meaningful output 139 | param.name = getChildName(curNode->children); 140 | } 141 | else if ("FunctionParameterType") { 142 | // Actual processing of the parameter type 143 | value = getChildName(curNode->children); 144 | param.updateParameterType(value); 145 | } 146 | name.clear(); 147 | value.clear(); 148 | } 149 | } 150 | 151 | // Add the attributes to the PinParameter 152 | param.id = utils::sot(pinParameterAttrs["id"]); 153 | 154 | return param; 155 | } 156 | 157 | vector PinPyModel::processFunctionParameters(xmlNode *node) { 158 | vector params; 159 | xmlNode *curNode = 0; 160 | string name, value; 161 | 162 | if (!node->children) 163 | return params; 164 | 165 | for(curNode = node->children; curNode; curNode = curNode->next) { 166 | if (XML_ELEMENT_NODE == curNode->type) { 167 | name = (char *)curNode->name; 168 | if ("FunctionParameter" == name) 169 | params.push_back(processFunctionParameter(curNode)); 170 | name.clear(); 171 | } 172 | } 173 | return params; 174 | } 175 | 176 | 177 | PinFunctionPrototype PinPyModel::processPinFunctionPrototype(xmlNode *node) { 178 | string name, value; 179 | 180 | PinFunctionPrototype proto; 181 | xmlNode *curNode = 0; 182 | 183 | for(curNode = node; curNode; curNode = curNode->next) { 184 | if (XML_ELEMENT_NODE == curNode->type) { 185 | name = (char *)curNode->name; 186 | if ("FunctionName" == name) 187 | proto.setName(getChildName(curNode->children)); 188 | else if ("FunctionAddress" == name) 189 | proto.setAddress(utils::sot(getChildName(curNode->children))); 190 | else if ("FunctionParameters" == name) 191 | proto.setFunctionParameters(processFunctionParameters(curNode)); 192 | else if ("FunctionReturnType" == name) 193 | proto.setReturnType(processReturnType(curNode)); 194 | name.clear(); 195 | } 196 | } 197 | 198 | return proto; 199 | } 200 | 201 | PinFunction PinPyModel::processPinPyFunction(xmlNode *node) { 202 | string name, value; 203 | map pinPyFunctionAttrs = getAttributes(node); 204 | 205 | PinFunction pinFunction; 206 | 207 | xmlNode *curNode = 0; 208 | // walk and get the checks 209 | if (node->children) { 210 | for (curNode = node->children; curNode; curNode = curNode->next) { 211 | if (XML_ELEMENT_NODE == curNode->type) { 212 | name = (char *)curNode->name; 213 | if ("FunctionPrototype" == name) { 214 | // Extract the prototype information 215 | pinFunction.setPrototype(processPinFunctionPrototype(curNode->children)); 216 | } 217 | else if ("BeforeCall" == name || "AfterCall" == name || "ReplaceCall" == name) { 218 | // Ouch! BadCode 101 - 219 | if (curNode->children && curNode->children->children) { 220 | xmlNode *childrenNode = curNode->children->children; 221 | for (;childrenNode; childrenNode = childrenNode->next) { 222 | if (childrenNode) 223 | pinFunction.setAction(name, processPinPyAction(childrenNode)); 224 | } 225 | } 226 | } 227 | name.clear(); 228 | value.clear(); 229 | } 230 | } 231 | } 232 | 233 | pinFunction.setName(pinPyFunctionAttrs["name"]); 234 | return pinFunction; 235 | } 236 | 237 | // Walking the XML DOM, and process every 'PinPyFunction' 238 | void PinPyModel::walk(xmlNode *node) { 239 | xmlNode *curNode = 0; 240 | string name; 241 | 242 | for (curNode = node; curNode; curNode = curNode->next) { 243 | bool processChildren = true; 244 | if (XML_ELEMENT_NODE == curNode->type) { 245 | name = (char *)curNode->name; 246 | if ("PinPyFunction" == name) { 247 | functions.push_back(new PinFunction(processPinPyFunction(curNode))); 248 | processChildren = false; 249 | name.clear(); 250 | } 251 | } 252 | if (processChildren) 253 | walk(curNode->children); 254 | } 255 | } 256 | 257 | void PinPyModel::consolidateModel() { 258 | for (vector::const_iterator iter=functions.begin(); iter != functions.end(); ++iter) { 259 | unsigned int functionId = utils::hash((*iter)->getName()); 260 | if (functionsHash.find(functionId) == functionsHash.end()) 261 | functionsHash[functionId] = *iter; 262 | } 263 | } 264 | 265 | bool PinPyModel::load(const map& conf) { 266 | map::const_iterator inputMapIterator = conf.find("input"), pythonMapIterator = conf.find("python-path"); 267 | 268 | confFile = inputMapIterator->second; 269 | if (pythonMapIterator != conf.end()) 270 | pythonPath = pythonMapIterator->second; 271 | 272 | xmlDoc *doc = 0; 273 | xmlNode *root_element = 0; 274 | 275 | doc = xmlReadFile(confFile.c_str(), 0, XML_PARSE_NOBLANKS|XML_PARSE_NOCDATA|XML_PARSE_NONET); 276 | if (doc == 0) { 277 | cout << "[ERROR] Could not parse file: " << confFile << endl; 278 | failure = true; 279 | return false; 280 | } 281 | 282 | // Let's validate the XML with a provided XSD 283 | 284 | // Parse the content of the XML file, and build the model 285 | root_element = xmlDocGetRootElement(doc); 286 | walk(root_element); 287 | xmlFreeDoc(doc); 288 | xmlCleanupParser(); 289 | 290 | // Consolidate the model 291 | consolidateModel(); 292 | 293 | // Initialize Python 294 | pyHelper.initialize(); 295 | 296 | return true; 297 | } 298 | 299 | 300 | // Iterate through the the different scripts, and make sure they compile 301 | // properly. 302 | void PinPyModel::preparePythonScripts(const string& paths) { 303 | pyHelper.setPath(paths); 304 | } 305 | 306 | // Intent: testing the pytohn module 307 | void PinPyModel::runPython(const string& module, const string& fct) { 308 | pyHelper.run(module.c_str(), fct.c_str(), NULL); 309 | } 310 | 311 | 312 | // Install all the probes, pin will scan the binary to detect the prototypes 313 | // and install the trampolines 314 | void PinPyModel::hookFunctions() { 315 | // Set the overall PinPyModel communication with static class methods 316 | // Pin interaction has to be done with C++ static class methods, or C-style 317 | // functions 318 | pintool::PinHelper::setPinPyModel(this); 319 | pintool::PinHelper::setPyHelper(&pyHelper); 320 | 321 | // Loop through all the functions and install the hooks 322 | for (vector::iterator iter=functions.begin(); iter!=functions.end(); ++iter) { 323 | PinFunction* cur = *iter; 324 | const PinFunctionPrototype& proto = cur->getPrototype(); 325 | cout << "PinPyModel::hookFunctions- Hooking function " << proto.getName() << endl; 326 | 327 | // Register Routine to be called to instrument rtn, passing the current 328 | // PinFunction to the routine 329 | RTN_AddInstrumentFunction(pintool::PinHelper::routine, (void *)(&(*cur))); 330 | } 331 | 332 | // Register Fini to be called when the application exits 333 | PIN_AddFiniFunction(pintool::PinHelper::fini, 0); 334 | } 335 | 336 | -------------------------------------------------------------------------------- /src/pinpy.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef PINPY_H 21 | #define PINPY_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include "pinfunction.h" 32 | #include "knowledge/knowledge.h" 33 | #include "pin/pintool.h" 34 | #include "python/pyhelper.h" 35 | 36 | #define DEBUG_PINTOOL 1 37 | 38 | namespace pinpy { 39 | 40 | class PinPyModel { 41 | bool failure; 42 | std::string confFile; 43 | std::string pythonPath; 44 | 45 | std::vector functions; 46 | std::map functionsHash; 47 | 48 | // Handle of pin/python 49 | pintool::PinHelper pinHelper; 50 | py::PyHelper pyHelper; 51 | knowledge::Knowledge mining; 52 | 53 | PinPyModel(const PinPyModel&) {} 54 | PinPyModel& operator=(const PinPyModel& ) { 55 | return *this; 56 | } 57 | public: 58 | PinPyModel() : failure(false) {} 59 | 60 | inline bool failed () const {return failure;} 61 | bool load(const std::map&); 62 | 63 | ~PinPyModel() { 64 | for (std::vector::iterator iter=functions.begin(); iter!=functions.end(); ++iter) 65 | if (*iter) 66 | delete *iter; 67 | } 68 | 69 | py::PyHelper& getPyHelper() { 70 | return pyHelper; 71 | } 72 | 73 | inline std::vector& getFunctions() { 74 | return functions; 75 | } 76 | 77 | knowledge::Knowledge& getKnowledge() { 78 | return mining; 79 | } 80 | 81 | // Prepare python scripts 82 | void preparePythonScripts(const std::string& paths=""); 83 | 84 | // Execute PIN probes 85 | void hookFunctions(); 86 | 87 | public: 88 | // Testing interfaces 89 | void runPython(const std::string& module, const std::string& fct); 90 | 91 | private: 92 | void walk(xmlNode *); 93 | PinFunction processPinPyFunction(xmlNode *); 94 | PinPyAction processPinPyAction(xmlNode *); 95 | PinParameter processReturnType(xmlNode *); 96 | PinParameter processFunctionParameter(xmlNode *); 97 | std::vector processFunctionParameters(xmlNode *); 98 | PinFunctionPrototype processPinFunctionPrototype(xmlNode *); 99 | 100 | // Consolidate references in the model 101 | void consolidateModel(); 102 | 103 | }; 104 | 105 | 106 | } 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/pinpyaction.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef PINACTION_H 21 | #define PINACTION_H 22 | 23 | #include 24 | #include 25 | 26 | namespace pinpy { 27 | 28 | // Types of actions supported by the current model 29 | enum ActionType { 30 | BEFORE_CALL, 31 | AFTER_CALL, 32 | REPLACE_CALL, 33 | NIL_ACTION 34 | }; 35 | 36 | struct PinPyCall { 37 | std::string module; 38 | std::string function; 39 | std::string message; 40 | 41 | }; 42 | 43 | // PinAction contains the information to perform one of the 44 | // actions delcared in the XML definition. 45 | // Example of actions: log, custom script call, etc. 46 | struct PinPyAction { 47 | unsigned int id; 48 | std::string name; 49 | 50 | // Python call information 51 | PinPyCall pyCall; 52 | 53 | // Need to hold a pointer to the 'execution driver' 54 | // PythonCaller *pyScript; 55 | }; 56 | 57 | 58 | } 59 | 60 | #endif -------------------------------------------------------------------------------- /src/python/pyhelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include "Python.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "pyhelper.h" 27 | #include "pinfunctionprototype.h" 28 | #include "utils.h" 29 | 30 | using namespace std; 31 | using namespace pinpy; 32 | using namespace utils; 33 | 34 | namespace py { 35 | 36 | //ofstream out("./python-helper.txt"); 37 | 38 | 39 | PyHelper::PyHelper() 40 | : failure(false) { 41 | Py_Initialize(); 42 | } 43 | 44 | void PyHelper::initialize() { 45 | if (!Py_IsInitialized()) { 46 | failure = true; 47 | } 48 | } 49 | 50 | void PyHelper::setPath(const vector& paths) { 51 | pythonPaths = paths; 52 | // Append the paths to the python environment 53 | if (!Py_IsInitialized()) { 54 | failure = true; 55 | } 56 | else { 57 | const string sysPath = Py_GetPath(); 58 | #ifdef _MSC_VER 59 | const string pathSeparator = ";"; 60 | #else 61 | const string pathSeparator = ":"; 62 | #endif 63 | if (paths.size() > 0) { 64 | finalSysPath = sysPath; 65 | for (vector::const_iterator iter=paths.begin(); iter!=paths.end(); ++iter) { 66 | finalSysPath += pathSeparator + *iter; 67 | } 68 | if (finalSysPath.size() > 0) { 69 | PySys_SetPath(const_cast(finalSysPath.c_str())); 70 | } 71 | } 72 | } 73 | } 74 | 75 | void PyHelper::setPath(const string& paths) { 76 | if (!Py_IsInitialized()) { 77 | failure = true; 78 | } 79 | else { 80 | // hack to reload the module 81 | addedPath = paths; 82 | 83 | const string sysPath = Py_GetPath(); 84 | #ifdef _MSC_VER 85 | const string pathSeparator = ";"; 86 | #else 87 | const string pathSeparator = ":"; 88 | #endif 89 | if (paths.size() > 0) { 90 | finalSysPath = sysPath + pathSeparator + paths; 91 | 92 | if (finalSysPath.size() > 0) { 93 | PySys_SetPath(const_cast(finalSysPath.c_str())); 94 | } 95 | } 96 | } 97 | } 98 | 99 | void PyHelper::run(const char *module, const char *function, PyObject *dict) const { 100 | PyObject *pName=0, *pModule=0, *pDict=0, *pFunc=0, *pValue=0, *pArgs=0, *pReloadedModule=0; 101 | 102 | // Build the name object 103 | pName = PyString_FromString(module); 104 | 105 | // Load the module object 106 | pModule = PyImport_Import(pName); 107 | if (!pModule) { 108 | PyErr_Print(); 109 | if (pName != NULL) { 110 | Py_DECREF(pName); 111 | } 112 | return; 113 | } 114 | 115 | /*ofstream out("./python-relad.txt", ios_base::app); 116 | out << "Process with loading of function: " << module << "::" << function << flush << endl; 117 | */ 118 | // pDict is a borrowed reference 119 | pModule = PyImport_ReloadModule(pModule); 120 | pDict = PyModule_GetDict(pModule); 121 | 122 | /* 123 | TODO: debug this routine so that we can dynamically check when the scripts change. 124 | 125 | // Check if reload is necessary 126 | PyObject *moduleFileName = PyDict_GetItemString(pDict, "__file__"); 127 | if (moduleFileName && PyString_Check(moduleFileName)){ 128 | string moduleFileNameString = PyString_AsString(moduleFileName); 129 | utils::replace(moduleFileNameString, ".pyc", ".py"); 130 | unsigned int h = getFileHash(moduleFileNameString); 131 | 132 | out << "Hash computed for " << moduleFileNameString << " = " << h << endl << flush; 133 | 134 | map::iterator iter = moduleHash.find(moduleFileNameString); 135 | 136 | out << "find passed.?" << endl << flush; 137 | 138 | if (iter != moduleHash.end()) { 139 | unsigned int hh = iter->second; 140 | 141 | out << "---> " << hh << endl << flush; 142 | 143 | if (hh != h) { 144 | // reload the module, store the hash of the current file 145 | pModule = PyImport_ReloadModule(pModule); 146 | pDict = PyModule_GetDict(pModule); 147 | moduleHash[moduleFileNameString] = h; 148 | out << "Changed hash for " << moduleFileNameString << " from " << hh << " to " << h << flush << endl; 149 | } 150 | } 151 | else 152 | moduleHash[moduleFileNameString] = h; 153 | } 154 | out << flush; 155 | */ 156 | 157 | // pFunc is also a borrowed reference 158 | pFunc = PyDict_GetItemString(pDict, function); 159 | 160 | if (pFunc && PyCallable_Check(pFunc)) { 161 | // Prepare the argument list for the call 162 | // The only argument we pass is a dictionary object 163 | if (dict){ 164 | pArgs = PyTuple_New(1); 165 | PyTuple_SetItem(pArgs, 0, dict); 166 | } 167 | // Call the function 168 | pValue = PyObject_CallObject(pFunc, pArgs); 169 | if (pArgs != NULL) { 170 | Py_DECREF(pArgs); 171 | } 172 | 173 | if (pValue != NULL) { 174 | // printf("Return of call : %d\n", PyInt_AsLong(pValue)); 175 | Py_DECREF(pValue); 176 | } else { 177 | PyErr_Print(); 178 | } 179 | } else { 180 | cerr << "PyHelper::run- Failed to load function \"" << function << "\" from module \"" << module << "\"" << endl; 181 | } 182 | 183 | // Clean up 184 | Py_DECREF(pModule); 185 | Py_DECREF(pName); 186 | } 187 | 188 | PyHelper::~PyHelper() { 189 | Py_Finalize(); 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/python/pyhelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef PYHELPER_H 21 | #define PYHELPER_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include "pinfunctionprototype.h" 27 | 28 | namespace py { 29 | 30 | class PyHelper { 31 | bool failure; 32 | std::string finalSysPath; 33 | std::map moduleHash; 34 | std::vector pythonPaths; 35 | std::string addedPath; 36 | 37 | PyHelper(const PyHelper&) {} 38 | PyHelper& operator=(const PyHelper& ) { 39 | return *this; 40 | } 41 | 42 | public: 43 | PyHelper(); 44 | 45 | void initialize(); 46 | 47 | void setPath(const std::string& paths); 48 | void setPath(const std::vector& paths); 49 | 50 | void run(const char *module, const char *function, PyObject *dict) const; 51 | 52 | inline bool failed() const { 53 | return failure; 54 | } 55 | 56 | ~PyHelper(); 57 | }; 58 | 59 | 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #include 21 | #include "utils.h" 22 | using namespace std; 23 | 24 | namespace utils { 25 | 26 | void replace(string& where, const string& what, const string& by) { 27 | for (string::size_type i = where.find(what); i != string::npos; i = where.find(what, i + by.size())) 28 | where.replace(i, what.size(), by); 29 | } 30 | 31 | vector split(const string& str, const string& delimiters) { 32 | vector tokens; 33 | string::size_type lastPos = str.find_first_not_of(delimiters, 0); 34 | string::size_type pos = str.find_first_of(delimiters, lastPos); 35 | 36 | while (string::npos != pos || string::npos != lastPos) { 37 | tokens.push_back(str.substr(lastPos, pos - lastPos)); 38 | lastPos = str.find_first_not_of(delimiters, pos); 39 | pos = str.find_first_of(delimiters, lastPos); 40 | } 41 | return tokens; 42 | } 43 | 44 | unsigned int hash(const string& str) { 45 | unsigned int hash = 0; 46 | unsigned int x = 0; 47 | for(size_t i = 0; i < str.length(); i++) { 48 | hash = (hash << 4) + str[i]; 49 | if((x = hash & 0xF0000000L) != 0) 50 | hash ^= (x >> 24); 51 | hash &= ~x; 52 | } 53 | return hash; 54 | } 55 | 56 | bool contains(const string& where, const string& what) { 57 | return string::npos != where.find(what); 58 | } 59 | 60 | unsigned int getFileHash(const string& filename) { 61 | ifstream myfile(filename.c_str()); 62 | string current, complete; 63 | if (myfile.is_open()) { 64 | while (myfile.good()) { 65 | getline (myfile, current); 66 | complete += current; 67 | } 68 | myfile.close(); 69 | } 70 | if (complete.size() > 0) 71 | return hash(complete); 72 | return 0; 73 | } 74 | 75 | 76 | } -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | PinPy -- Shake your hooks with some Python! 3 | by Romain Gaucher - http://rgaucher.info 4 | 5 | Copyright (c) 2011-2012 Romain Gaucher @rgaucher 6 | Haris Andrianakis @tl0gic 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | */ 20 | #ifndef UTILS_H 21 | #define UTILS_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace utils { 28 | 29 | void replace(std::string&, const std::string&, const std::string&); 30 | 31 | template 32 | std::string tos(const T& t, std::ios_base& (*f)(std::ios_base&)=std::dec, std::ios_base& (*c)(std::ios_base&)=std::nouppercase) { 33 | std::stringstream ss; 34 | ss << c << f << t; 35 | return ss.str(); 36 | } 37 | 38 | template 39 | T sot(const std::string& str) { 40 | T output; 41 | std::istringstream ss(str); 42 | ss >> output; 43 | return output; 44 | } 45 | 46 | bool contains(const std::string&, const std::string&); 47 | 48 | std::vector split(const std::string&, const std::string&); 49 | 50 | unsigned int hash(const std::string&); 51 | 52 | unsigned int getFileHash(const std::string&); 53 | 54 | } 55 | 56 | #endif --------------------------------------------------------------------------------