{ "info": { "author": "justfoxing", "author_email": "justfoxingprojects@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python" ], "description": "Ghidra Bridge\n=====================\nGhidra is great, and I like scripting as much of my RE as possible. But Ghidra's Python scripting is based on Jython, which isn't in a great state these days (not that IDA's Python environment is any better...). Installing new packages is a hassle, if they can even run in a Jython environment, and it's only going to get worse as Python 2 slowly gets turned off.\n\nSo Ghidra Bridge is an effort to sidestep that problem - instead of being stuck in Jython, set up an RPC proxy for Python objects, so we can call into Ghidra/Jython-land to get the data we need, then bring it back to a more up-to-date Python with all the packages you need to do your work. \n\nThe aim is to be as transparent as possible, so once you're set up, you shouldn't need to know if an object is local or from the remote Ghidra - the bridge should seamlessly handle getting/setting/calling against it.\n\nHow to use for Ghidra\n======================\n\n1. Add the path to the ghidra_bridge directory as a script directory in the Ghidra Script Manager (the \"3 line\" button left of the big red \"plus\" at the top of the Script Manager)\n2. Run ghidra_bridge_server.py from the Ghidra Script Manager\n3. Install ghidra_bridge in the client python environment (packaged at https://pypi.org/project/ghidra-bridge/):\n```\npip install ghidra_bridge\n```\n4. From the client python:\n```\nimport ghidra_bridge\nwith ghidra_bridge.GhidraBridge(namespace=globals()):\n print(getState().getCurrentAddress().getOffset())\n ghidra.program.model.data.DataUtilities.isUndefinedData(currentProgram, currentAddress)\n```\nor\n```\nimport ghidra_bridge\nb = ghidra_bridge.GhidraBridge(namespace=globals()) # creates the bridge and loads the flat API into the global namespace\nprint(getState().getCurrentAddress().getOffset())\n# ghidra module implicitly loaded at the same time as the flat API\nghidra.program.model.data.DataUtilities.isUndefinedData(currentProgram, currentAddress)\n```\n\nSecurity warning\n=====================\nBe aware that when running, a Ghidra Bridge server effectively provides code execution as a service. If an attacker is able to talk to the port Ghidra Bridge is running on, they can trivially gain execution with the privileges Ghidra is run with. \n\nAlso be aware that the protocol used for sending and receiving Ghidra Bridge messages is unencrypted and unverified - a person-in-the-middle attack would allow complete control of the commands and responses, again providing trivial code execution on the server (and with a little more work, on the client). \n\nBy default, the Ghidra Bridge server only listens on localhost to slightly reduce the attack surface. Only listen on external network addresses if you're confident you're on a network where it is safe to do so. Additionally, it is still possible for attackers to send messages to localhost (e.g., via malicious javascript in the browser, or by exploiting a different process and attacking Ghidra Bridge to elevate privileges). You can mitigate this risk by running Ghidra Bridge from a Ghidra server with reduced permissions (a non-admin user, or inside a container), by only running it when needed, or by running on non-network connected systems.\n\nRemote eval\n=====================\nGhidra Bridge is designed to be transparent, to allow easy porting of non-bridged scripts without too many changes. However, if you're happy to make changes, and you run into slowdowns caused by running lots of remote queries (e.g., something like `for function in currentProgram.getFunctionManager().getFunctions(): doSomething()` can be quite slow with a large number of functions as each function will result in a message across the bridge), you can make use of the bridge.remote_eval() function to ask for the result to be evaluated on the bridge server all at once, which will require only a single message roundtrip.\n\nThe following example demonstrates getting a list of all the names of all the functions in a binary:\n```\nimport ghidra_bridge \nb = ghidra_bridge.GhidraBridge(namespace=globals())\nname_list = b.bridge.remote_eval(\"[ f.getName() for f in currentProgram.getFunctionManager().getFunctions(True)]\")\n```\n\nIf your evaluation is going to take some time, you might need to use the timeout_override argument to increase how long the bridge will wait before deciding things have gone wrong.\n\nIf you need to supply an argument for the remote evaluation, you can provide arbitrary keyword arguments to the remote_eval function which will be passed into the evaluation context as local variables. The following argument passes in a function:\n```\nimport ghidra_bridge \nb = ghidra_bridge.GhidraBridge(namespace=globals())\nfunc = currentProgram.getFunctionManager().getFunctions(True).next()\nmnemonics = b.bridge.remote_eval(\"[ i.getMnemonicString() for i in currentProgram.getListing().getInstructions(f.getBody(), True)]\", f=func)\n```\nAs a simplification, note also that the evaluation context has the same globals loaded into the \\_\\_main\\_\\_ of the script that started the server - in the case of the Ghidra Bridge server, these include the flat API and values such as the currentProgram.\n\nInteractive mode\n=====================\nNormally, Ghidra scripts get an instance of the Ghidra state and current\\* variables (currentProgram, currentAddress, etc) when first started, and it doesn't update while the script runs. However, if you run the Ghidra Python interpreter, that updates its state with every command, so that currentAddress always matches the GUI.\n\nTo reflect this, GhidraBridge will automatically attempt to determine if you're running the client in an interactive environment (e.g., the Python interpreter, iPython) or just from a script. If it's an interactive environment, it'll register an event listener with Ghidra and perform some dubious behind-the-scenes shenanigans to make sure that the state is updated with GUI changes to behave like the Ghidra Python interpreter. It'll also replace `help()` with one that reaches out to use Ghidra's help across the bridge if you give it a bridged object.\n\nYou shouldn't have to care about this, but if for some reason the auto-detection doesn't give you the result you need, you can specify the boolean interactive_mode argument when creating your client GhidraBridge to force it on or off as required.\n\nHow it works\n=====================\nbridge.py contains a py2/3 compatible python object RPC proxy. One python environment sets up a server on a port, which clients connect to. The bridge provides a handful of commands to carry out remote operations against python objects in the other environment.\n\nA typical first step is remote_import() with a module to load in the target environment. This will make the RPC call to the remote bridge, which will load the module, then create a BridgeHandle to keep it alive and reference it across the bridge. It'll then return it to the local bridge, along with a list of the callable and non-callable attributes of the module.\n\nAt the local bridge, this will be deserialized into a BridgedObject, which overrides \\_\\_getattribute\\_\\_ and \\_\\_setattr\\_\\_ to catch any get/set to the attribute fields, and proxy them back across to the remote bridge, using the bridge handle reference so it knows which module (or other object) we're talking about.\n\nThe \\_\\_getattribute\\_\\_ override also affects callables, so doing bridged_obj.func() actually returns a BridgedCallable object, which is then invoked (along with any args/kwargs in use). This packs the call parameters off to the remote bridge, which identifies the appropriate object and invokes the call against it, then returns the result.\n\nThe bridges are symmetric, so the local bridge is able to send references to local python objects to the remote bridge, and have them used over there, with interactions being sent back to the local bridge (e.g., providing a callback function as an argument works).\n\nFinally, there's a few other miscellaneous features to make life easier - bridged objects which are python iterators/iterables will behave as iterators/iterables in the remote environment, and bridged objects representing types can be inherited from to make your own subclasses of them (note that this will actually create the subclass in the remote environment - this is designed so you can create types to implement some of Ghidra's Java interfaces for callbacks/listeners/etc, so it was easier to make sure they behave if they're created in the Jython environment).\n\nDesign principles\n=====================\n* Needs to be run in Ghidra/Jython 2.7 and Python 3\n* Needs to be easy to install in Ghidra - no pip install, just add a single directory \n(these two requirements ruled out some of the more mature Python RPC projects I looked into)\n\nTested\n=====================\n* Tested and working on Ghidra 9.0.4(Jython 2.7.1) <-> Python 3.7.3 on Windows\n* Automatically tested on Ghidra 9.0(Jython 2.7.1) <-> Python 3.5.3 on Linux (bskaggs/ghidra docker image)\n\nTODO\n=====================\n* Ghidra plugin for server control (cleaner start/stop, port selection, easy packaging/install)\n* Handle server/client teardown cleanly\n* Exceptions - pull traceback info in the exceptions we handle for pushing back\n* Better transport/serialization (JSON/TCP just feels wrong)\n* Keep stats of remote queries, so users can ID the parts of their scripts causing the most remote traffic for optimisation\n* Examples\n * Jupyter notebook\n* Better threadpool control (don't keep all threads around forever, allow some to die off)\n\nContributors\n=====================\n* Thx @fmagin for better iPython support, and much more useful reprs!\n* Thanks also to @fmagin for remote_eval, allowing faster remote processing for batch queries!\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/justfoxing/ghidra_bridge", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "ghidra-bridge", "package_url": "https://pypi.org/project/ghidra-bridge/", "platform": "", "project_url": "https://pypi.org/project/ghidra-bridge/", "project_urls": { "Homepage": "https://github.com/justfoxing/ghidra_bridge" }, "release_url": "https://pypi.org/project/ghidra-bridge/0.0.7/", "requires_dist": null, "requires_python": "", "summary": "RPC bridge from Python to Ghidra Jython", "version": "0.0.7" }, "last_serial": 5594982, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "7a2b464d6e36471e3926ae33a702ca1a", "sha256": "56242ab7027190bc4cb1ed1f550adca2e026134053bfae4cc63a74bedeb80564" }, "downloads": -1, "filename": "ghidra_bridge-0.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "7a2b464d6e36471e3926ae33a702ca1a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 14222, "upload_time": "2019-04-27T02:53:33", "url": "https://files.pythonhosted.org/packages/ad/45/289378674829dafbbbb7407140816a3651c166715667e8bec058c4e6c88c/ghidra_bridge-0.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "172d7750414e26bc0ee51fa942a9303a", "sha256": "e74135a836df7b81fd1fa192afd34ae99b06fd997c9913c12108e92fe1da9ca1" }, "downloads": -1, "filename": "ghidra_bridge-0.0.1.tar.gz", "has_sig": false, "md5_digest": "172d7750414e26bc0ee51fa942a9303a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12377, "upload_time": "2019-04-27T02:53:37", "url": "https://files.pythonhosted.org/packages/b4/40/3410b81614492204f77bea82bcee59e6971437323d2930878915610d85c1/ghidra_bridge-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "b761c6843c4e183e838ba9b7602bfa0f", "sha256": "726fd40db571752ec41e41db59984d76598094fef0f5722b5626fa4cf224f113" }, "downloads": -1, "filename": "ghidra_bridge-0.0.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "b761c6843c4e183e838ba9b7602bfa0f", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13212, "upload_time": "2019-05-14T10:46:28", "url": "https://files.pythonhosted.org/packages/44/93/d74b0d53583cb1b4414a9eb763e9fd070282940d97bd31b21fdca8870e23/ghidra_bridge-0.0.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "764dea3e0cac1df73f8dafeb535af93d", "sha256": "1d666b8c08794b016e90b01ea717b421ad693243564e7741717d0eca599dd961" }, "downloads": -1, "filename": "ghidra_bridge-0.0.2.tar.gz", "has_sig": false, "md5_digest": "764dea3e0cac1df73f8dafeb535af93d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13399, "upload_time": "2019-05-14T10:46:30", "url": "https://files.pythonhosted.org/packages/e9/f3/1723238e475d44a9fc847bec01fa38751de1f61407cfc9599d85d257ea78/ghidra_bridge-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "b8a71be9b92c9c890d1878100d5b8ac8", "sha256": "de2f8340a89a55225b1977249036a3d80ea02f849a7b27416423884703471943" }, "downloads": -1, "filename": "ghidra_bridge-0.0.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "b8a71be9b92c9c890d1878100d5b8ac8", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21067, "upload_time": "2019-05-29T11:50:08", "url": "https://files.pythonhosted.org/packages/bf/06/76900899ef27ac7879687a74472e5b27847a022419d57f2a65a9654243cb/ghidra_bridge-0.0.3-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "737e7768ac188a332afe6e1d8022cd86", "sha256": "ed4fb4f5daa9c7a04d4eae49eda523d4391a96b2fd2c1e689adc95b63302d603" }, "downloads": -1, "filename": "ghidra_bridge-0.0.3.tar.gz", "has_sig": false, "md5_digest": "737e7768ac188a332afe6e1d8022cd86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20889, "upload_time": "2019-05-29T11:50:09", "url": "https://files.pythonhosted.org/packages/d6/50/e4e24019fbb8fb01f727fd54853969e8f0c70027f8a80421764036fdca7d/ghidra_bridge-0.0.3.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "1ea555a06feccad5dcbd73814fd87875", "sha256": "341f2d6a99f56e2c34fa32f4a1991131d850aa04db7e0d3aa0aa6e914587718b" }, "downloads": -1, "filename": "ghidra_bridge-0.0.4-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "1ea555a06feccad5dcbd73814fd87875", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21371, "upload_time": "2019-06-01T01:45:45", "url": "https://files.pythonhosted.org/packages/fb/9f/4bb4e963bd7999c28dba56ea42522002e2cf42d64399c35d84d1b25941a7/ghidra_bridge-0.0.4-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c3f283648989c43ffe87013ddf1c9ed9", "sha256": "d1731574274a7cfc60267c1acd8312ba1f91e831db981c306ba1fe993ccc2dff" }, "downloads": -1, "filename": "ghidra_bridge-0.0.4.tar.gz", "has_sig": false, "md5_digest": "c3f283648989c43ffe87013ddf1c9ed9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21122, "upload_time": "2019-06-01T01:45:46", "url": "https://files.pythonhosted.org/packages/fa/ee/a3d1880b54367c5f646e80fb983760ceab401800dcc89caedfe824f79bbc/ghidra_bridge-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "fd95a3f73ea539611fa15cc8b35dd853", "sha256": "1e980ebb80b52872ad04fa87b66a5a62abcc72269e071db02ffd24deb6175904" }, "downloads": -1, "filename": "ghidra_bridge-0.0.5-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "fd95a3f73ea539611fa15cc8b35dd853", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 23037, "upload_time": "2019-06-01T05:38:21", "url": "https://files.pythonhosted.org/packages/58/39/f8eb59d50709afd51874d0342e3d9f362356b49f09f83ca60778d141f26f/ghidra_bridge-0.0.5-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "69ae290618c96ee4425c3c092f623464", "sha256": "ccc9b8abb6e0b2bf269eeabf9caf2225fd040d0186dd0d13bf3dda3ef3b79e41" }, "downloads": -1, "filename": "ghidra_bridge-0.0.5.tar.gz", "has_sig": false, "md5_digest": "69ae290618c96ee4425c3c092f623464", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22706, "upload_time": "2019-06-01T05:38:22", "url": "https://files.pythonhosted.org/packages/21/82/c4cf02573b8d5412e857d735eff8a5e3a709e897e9082d2c1a4b71dacb9c/ghidra_bridge-0.0.5.tar.gz" } ], "0.0.6": [ { "comment_text": "", "digests": { "md5": "779b31dbdaef62b10249be2f000db920", "sha256": "e2dbd4ca05881571ca6d323e691822ac729edc46e396389c28ca6fa9b012ee2c" }, "downloads": -1, "filename": "ghidra_bridge-0.0.6-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "779b31dbdaef62b10249be2f000db920", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 23914, "upload_time": "2019-06-15T11:24:22", "url": "https://files.pythonhosted.org/packages/13/57/3e6af15bfe21579d3327ef19c20629a43d3ba8550300c4264bd0bbf57a80/ghidra_bridge-0.0.6-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "916c5c083c25a23f4d8eec64608b6c6a", "sha256": "9681707585781b120749b9654438a4046f4bc49eea0f52b4776515c5faddf992" }, "downloads": -1, "filename": "ghidra_bridge-0.0.6.tar.gz", "has_sig": false, "md5_digest": "916c5c083c25a23f4d8eec64608b6c6a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23541, "upload_time": "2019-06-15T11:24:23", "url": "https://files.pythonhosted.org/packages/75/8f/2ed7fcccd6e41da2a58935fe1fbba82306506a2ed570a0a91c9ee088ec7e/ghidra_bridge-0.0.6.tar.gz" } ], "0.0.7": [ { "comment_text": "", "digests": { "md5": "3a16071bc1514d1936010bd46aa306c6", "sha256": "550ccbd4833b22d12f1439d29a6e66565fcd277607035eb5237abf580f283c05" }, "downloads": -1, "filename": "ghidra_bridge-0.0.7-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3a16071bc1514d1936010bd46aa306c6", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 26501, "upload_time": "2019-07-28T06:52:41", "url": "https://files.pythonhosted.org/packages/ba/ff/b62e8c812f93e2931be999f46d2b3331f1acf3c33fe8aec88c2430231443/ghidra_bridge-0.0.7-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0989bd9fec3864740232a185478ef6b5", "sha256": "a20e41483e1c3f1cd1b6267736eeeda12e9fddf7606d2f7c28cca053ba00d137" }, "downloads": -1, "filename": "ghidra_bridge-0.0.7.tar.gz", "has_sig": false, "md5_digest": "0989bd9fec3864740232a185478ef6b5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27370, "upload_time": "2019-07-28T06:52:42", "url": "https://files.pythonhosted.org/packages/e4/bc/83c8ef09d1e303873719fe2b77aee7ad3cfa89a2148b43bdfec2b0fe54d7/ghidra_bridge-0.0.7.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "3a16071bc1514d1936010bd46aa306c6", "sha256": "550ccbd4833b22d12f1439d29a6e66565fcd277607035eb5237abf580f283c05" }, "downloads": -1, "filename": "ghidra_bridge-0.0.7-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3a16071bc1514d1936010bd46aa306c6", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 26501, "upload_time": "2019-07-28T06:52:41", "url": "https://files.pythonhosted.org/packages/ba/ff/b62e8c812f93e2931be999f46d2b3331f1acf3c33fe8aec88c2430231443/ghidra_bridge-0.0.7-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0989bd9fec3864740232a185478ef6b5", "sha256": "a20e41483e1c3f1cd1b6267736eeeda12e9fddf7606d2f7c28cca053ba00d137" }, "downloads": -1, "filename": "ghidra_bridge-0.0.7.tar.gz", "has_sig": false, "md5_digest": "0989bd9fec3864740232a185478ef6b5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27370, "upload_time": "2019-07-28T06:52:42", "url": "https://files.pythonhosted.org/packages/e4/bc/83c8ef09d1e303873719fe2b77aee7ad3cfa89a2148b43bdfec2b0fe54d7/ghidra_bridge-0.0.7.tar.gz" } ] }