Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDA blocking while python commands are executed #12

Open
fmagin opened this issue Feb 20, 2018 · 13 comments
Open

IDA blocking while python commands are executed #12

fmagin opened this issue Feb 20, 2018 · 13 comments

Comments

@fmagin
Copy link

fmagin commented Feb 20, 2018

I am aware that this is an issue with regular Python in IDA too, so I am assuming this is an issue with IDA itself,but it might be possible to work around it with the IPython hooking capabilities.

When running any command in the Python shell the IDA GUI is not usable until the command terminates. At least in my case when using angr it happens fairly often that I accidentally run commands which will take basically forever (state exploration without proper limits).

My idea was to find some way to set a timeout on any executed cell in IPython by leveraging the hooking points[0] or code transformations[1] IPython provides.

The ways to implement timeouts in Python[2] seem to be either by using multiprocessing or signal handling, the latter of which is not available on non-UNIX systems. I haven't found a way yet to do this properly via multi processing (or maybe multi threading?) but any input would be appreciated.

[0] https://ipython.readthedocs.io/en/5.x/api/generated/IPython.core.hooks.html
[1] https://ipython.readthedocs.io/en/5.x/config/inputtransforms.html
[2] https://stackoverflow.com/questions/492519/timeout-on-a-function-call

@marc-etienne
Copy link
Member

marc-etienne commented Feb 21, 2018

I agree this is frustrating problem sometimes, but IMO it would be quite difficult to implement in IPyIDA. IDA is a single threaded application and so is Python. Furthermore, both runs on the same thread. It could be a feature request for Hex-Rays support. From what I understand, IDA's main loop looks like:

void main_loop() {
  while(1) {
     apply_pending_ui_update();
     run_pending_analysis();
     run_pending_python_commands();
  }
}

Even if we hack it with real (non-python) threads, it the IDA API wouldn't be thread-safe anyway so it would come with it's set of problems too.

@fmagin
Copy link
Author

fmagin commented Feb 21, 2018

I just learned that in the normal IDA Python you have the ability to interrupt a running python script. If you run something like while True: time.sleep(1) you get the popup "Running Python Script" and pressing Cancel in that Dialog Box interrupts the Code with a

Traceback (most recent call last):
  File "<string>", line 1, in <module>
KeyboardInterrupt: User interrupted

So basically that feature for the IPython shell would perfectly suffice and it seems to be possible to do.

@marc-etienne
Copy link
Member

You're right, there must be a way. I'll look into the IDAPython source to see how it's done.

@fmagin
Copy link
Author

fmagin commented Apr 11, 2018

Okay, I think I am starting to understand how IDAPython handles this:
The (only) line that throws that exception is in the function execution_t::on_trace which is being set as the trace callback via PyEval_SetTrace in this line which gets called from the actual Python VM as far as I understand.

The idea behind all that stuff is explained at the start of the code that deals with it (here)

What I don't understand yet is how it would be possible to trigger this from ipyida

@tmr232
Copy link
Contributor

tmr232 commented Jul 15, 2018

We may be able to use sys.settrace, but it might incur a performance penalty. Worth checking out.

@fmagin
Copy link
Author

fmagin commented Jul 15, 2018

I have been thinking about this a bit and also had to deal with it when writing https://github.com/ernw/binja-ipython. I could use multiple threads there and just inject an exception into the other thread, but this probably wont work here.

In general, at least to my knowledge, you can't interrupt a kernel that was for example started in a notebook and than attached to with a console from the console either, so this is more of a general problem with IPython. Maybe we can find out how the Jupyter Notebook interrupts the kernel when you use the menu button and use that approach? Potentially the kernel exposes some function that handles this and can be called from wherever but I am not that familiar with the IPython internals. Might be worth it to ask on their mailing list.

@tmr232
Copy link
Contributor

tmr232 commented Jul 16, 2018

We can probably head over and ask @minrk. The IPython community is generally friendly and helpful.

@minrk
Copy link

minrk commented Jul 18, 2018

Maybe we can find out how the Jupyter Notebook interrupts the kernel when you use the menu button and use that approach?

Ultimately it comes down to this method where we send signals in unix. This works in the notebook because the kernel is a subprocess, so we have a handle on the KernelManager. This is also why you lose this capability when you connect an additional console - the console doesn't have a handle on the process, only a network connection to the kernel. We have a special mechanism via Windows APIs for interrupt events, and it involves cooperation of both the client and the kernel.

@sfinktah
Copy link

Sorry, I've read all posts from the experts, and I hate to be "that guy", but ipyida is isn't usable without an Abort function. I realise that this is hard on many levels, the most obvious being that the UI thread immediately locks up so there's no way to process an interrupt request.

But setting that aside, and assuming the abort trigger is dispatched from an external source (lets just assume that such a signal can be sent), what barriers are there in getting IDA to co-operate?

What has to be reverse engineered and hooked in IDA? (I'm talking Windows here, as I assume this is not such a killer issue on other platforms). And I'm assuming that the signal won't be able to be sent from ipyida itself... but assuming a signal could be sent... what needs to be done?

@fmagin @tmr232 @marc-etienne

@fmagin
Copy link
Author

fmagin commented Mar 19, 2021

I am not using IDA much anymore because I moved to Ghidra, but the https://github.com/justfoxing/jfx_bridge_ida might be something that also serves your use case. I assume that if I need to an IPython environment with IDA again I will use that, I have good experiences with the https://github.com/justfoxing/ghidra_bridge project that uses the same underlying RPC implementation.

@sfinktah
Copy link

sfinktah commented May 9, 2021

@fmagin Interesting... I personally couldn't use Ghdira because it's so terribly horrific to look at, but if I had the time I would love to do an IDA remix of it (shortcuts & GUI reskin). Not that IDA is that great to look at, but at least it doesn't use capitals for it's disassembly.

I have a lot of respect for it's decompiler too, on the few occasions I've compared it to some rather ugly decompiles in IDA, it's produced some quite beautiful code with minimal information.

I'll check out justfoxing. I could probably live with IDA's inbuilt python console if I could actually read the tiny writing.

@fmagin
Copy link
Author

fmagin commented May 9, 2021

This is getting somewhat off topic, but there are already various keyboard configs out for Ghidra so it matches the IDA shortcuts. I have been using https://github.com/nullteilerfrei/reversing-class/blob/master/ghIDA.kbxml , but there isn't much of a difference between them anyway.

Somewhat on topic again: My experience with a Jupyter Kotlin kernel for Ghidra has shown me that while the jupyter-console or qtconsole refuses to send an interrupt signal, you can just create your own connection to send an interrupt signal and it works: GhidraJupyter/ghidra-jupyter-kotlin@eeffad0#diff-3b084b06f9eeb6adc5ec48c8205f434301daabefe3071971db39f4aa26b0a6dd

This might only work because the Kotlin Kernel uses multiple threads though, so it could be fundamentally impossible with the Jupyter Python kernel. I do recall that IDA changed something related to threading in the last few years, so maybe there is hope.

But at the end of the day neither Python nor IDA support this scenario very well, so I doubt it will ever get better than a bunch of hacks to make it tolerable.

@sfinktah
Copy link

sfinktah commented Sep 25, 2021

@fmagin Hmm... I wish I knew how it actually interrupted a kernel it didn't start. In "something completely different" I ended up making a web-ui for IDA as a demo for https://github.com/sfinktah/idarest75/ ... which while very pretty, will suffer from the identical inability to interrupt a running command.

Commands are executed via ida_kernwin.execute_sync and if the evaluated callable is a generator it will terminate when the requesting client disconnects (providing the callable hit a yield), but that was just luck.

As far as I can see, the main difference in "new" IDA regarding threads, is that it now prevents you from running non-thread-safe functions from any other thread but main. Possible the aforementioned execute_sync command is new, and there is a hint that ida_kernwin.cancel_exec_request will cancel an execute_sync request if that request is establish in a certain way. unfortunately the documentation is a tad confusing (sounds like it was ported from C/C++, unless python has developed a new operator while I wasn't looking)

I am still suspicious that this will only remove the request from the queue (if it hasn't already run).

MFF_NOWAIT = _ida_kernwin.MFF_NOWAIT
"""
Do not wait for the request to be executed. the caller should ensure
that the request is not destroyed until the execution completes. if
not, the request will be ignored. the request must be created using
the 'new' operator to use it with this flag. it can be used in
'cancel_exec_request()' . This flag can be used to delay the code
execution until the next UI loop run even from the main thread.
"""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants