IDA python over IPC


Author: David Zimmer
Date: 11.08.17 - 3:58am



Continuing on from a previous thought about IDASRVR and x64 IDA7.

Everytime I want a new command in IDASRVR i need to find the functionality in the C API, then write a new stub to make it accessible over my IPC mechanism. Its no surprise I have only implemented what I have needed as I have needed it.

The IDA Python API wraps a zillion more things than I have, can I just have my C IPC server call the python api and then pass me back the return value?

Thought two, with the differences between the C API and compile requirements between IDA 6.95 and IDA 7 and also between 32bit and 64bit databases do I even want to deal with the C API anymore. Can I just host the entire IPC server in python as a .py plugin?

I have been experimenting with both of these things.

The python IPC server was pretty straight forward to create. I use the same WM_COPYDATA mechanism as IDASRVR. This lets clients broadcast to enumerate all open server windows, is by default synchronous across processes and has a built in timeout I dont have to worry about. Even through its local and windows only its just the way to go for my needs. Only downside is you have to manually start it when you want to use it although this could be overcome I am sure.

So basically it listens on a hwnd, when a command comes in it uses python exec or eval to run it. Script output is returned to the IPC client through a reply() function.

Ok cool. Now what about the C IDASRVR plugin. Can I run arbitrary python commands from that? The answer again is yes.

I did a little digging and found a hexblog post that got me started. I had experimented with python embedding a little bit, so now I got to play some more.

One thing i noticed when embedding Python within an IDA plugin... IDA links against the python27.dll always. When i compiled my plugin in debug mode it would fail to load, but release mode was ok. It didnt matter which lib i linked against (python27_d.lib or python27.lib).

Turns out python.h has a little hidden surprise

#include python.h -> pyconfig.h -> ifdef _DEBUG pragma comment(lib,"python27_d.lib") 

The solution is:

#ifdef _DEBUG
	#undef _DEBUG
	#include python27/Python.h
	#define _DEBUG
#else
	#include python27/Python.h
#endif

Now when we receive a python command over IPC

case 40: //pycmd:hwnd:replace(cmd,":",chr(5))
	  if( argc != 2 ){msg("pycmd needs 2 args"); return -1;}
	  hPYClient = atoi(args[1]);                    //hwnd used in idasrvr.reply so we dont have to track in py
	  for(i=0;i<strlen(args[2]);i++){               //since we use : as a token transpose back..
		  if(args[2][i] == 5) args[2][i] = ':';     // use idasrvr.reply to ret vals to caller through ipc
	  }
	  PyGILState_STATE state = PyGILState_Ensure(); // Acquire the GIL
	  init_pyClient();                              // add our idasrvr extension to the pyEnv
	  PyRun_SimpleString(pyStub);
	  PyRun_SimpleString(args[2]);                  // execute the python code passed to us by remote client
	  PyGILState_Release(state);                    // Release the GIL


In order to return values back to our IPC client again we add a reply() function called from the script itself. Two caveats here:
  • Adding new functions in C to python seems to require a module import. This necessitates a bulky import line and module.function type call.
  • The underlying IPC mechanism has a limit of 1024 characters per chunk.
To keep user scripts simple, I want to take care of both of these details so they can remain small and concise. This is where the pyStub comes in:

import idasrvr

def chunkString(s,sz=1000):
    o = []
    while s:
        o.append(s[:sz])
        s = s[sz:]
    return o

def reply(message):
    #print "sending msg '%s'" % message
    message = str(message)

    if len(message) > 1000:
        chunks = chunkString(message)
        for c in chunks:
            idasrvr.reply(c)
    else:
        idasrvr.reply(message)


Now the client scripts can just simply call reply(). You can also see the hPyClient respond to hwnd has also been cached by the C code for simplicity.

So anyway..it works and it was pretty easy to implement. I still need to bench mark it to see how much of a hit I am taking. I am sure it will be slow, but It will be nice to have as a fall back for API i havent implemented in C yet. At the end of the day I am just one dude tinkering on what I need. One of the things I need is the ability to leverage other peoples work so here it is in all of its lazy sloppy glory!  *

I have added support for the pycmd handler in IDASRVR. The vb6 demo project has support for it already and it is as simple to use as the following:

resp = ida.pythonTest("reply('A'*1001)")



*This feels a little sloppy and slow, but I only have limited time and just need to get things done plus python is already loaded and installed with IDA. End of the day the C embedding was implemented in 60 lines of code, less than two hours time, and makes hundreds of new API available to me at zero additional cost. You cant ignore leverage like that.




Comments: (1)

On 07.30.20 - 2:33pm Dave wrote:
linking python into your plugin comes with more problems and slop..It wont work on all IDA versions without a relink because IDA may have changed which version it uses. I really hate plugin fragility especially when its out of my control. (which is why i started developing the plug-out method)

 
Leave Comment:
Name:
Email: (not shown)
Message: (Required)
Math Question: 61 + 98 = ? followed by the letter: A 



About Me
More Blogs
Main Site
Posts: (All)
2023 ( 4 )
2022 ( 5 )
2021 ( 2 )
2020 ( 5 )
2019 ( 6 )
2017 (5)
     IDA python over IPC
     dns wildcard blocking
     64bit IDA Plugins
     anterior lines
     misc news/updates
2016 (4)
     KANAL Mod
     Decoders again
     CDO.Message Breakpoints
     SysAnalyzer Updates
2015 (5)
     SysAnalyzer and Site Updates
     crazy decoder
     ida js w/dbg
     flash patching #2
     JS Graphing
2014 ( 5 )
2013 ( 9 )
2012 ( 13 )
2011 ( 19 )
2010 ( 11 )
2009 ( 1 )