Skip to content

Commit

Permalink
add pyrpc
Browse files Browse the repository at this point in the history
  • Loading branch information
doronz88 committed Apr 26, 2022
1 parent 2ff59e4 commit cfdceb6
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 30 deletions.
103 changes: 103 additions & 0 deletions fa/ida_launcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/python
import os
import socket
import subprocess
from collections import namedtuple

import IPython
import click
import rpyc
from termcolor import cprint

IDA_PLUGIN_PATH = os.path.abspath(os.path.join((os.path.dirname(__file__), 'ida_plugin.py')))

TerminalProgram = namedtuple('TerminalProgram', 'executable args')


def is_windows():
return os.name == 'nt'


SUPPORTED_TERMINALS = [
TerminalProgram(executable='kitty', args=['bash', '-c']),
TerminalProgram(executable='gnome-terminal', args=['-x', 'bash', '-c']),
TerminalProgram(executable='xterm', args=['-e']),
]


def get_free_port():
s = socket.socket()
s.bind(('', 0))
port = s.getsockname()[1]
s.close()
return port


def does_program_exist(program):
return 0 == subprocess.Popen(['which', program]).wait()


def execute_in_new_terminal(cmd):
if is_windows():
subprocess.Popen(cmd)
return

for terminal in SUPPORTED_TERMINALS:
if does_program_exist(terminal.executable):
subprocess.Popen([terminal.executable] + terminal.args + [' '.join(cmd)])
return


def get_client(ida, payload, loader=None, processor_type=None, accept_defaults=False, log_file_path=None):
port = get_free_port()
args = [ida]

if processor_type is not None:
args.append('-p{}'.format(processor_type))

if loader is not None:
args.append('-T{}'.format(loader))

if log_file_path is not None:
args.append('-L{}'.format(log_file_path))

if accept_defaults:
args.append('-A')

args.append('\'-S{} --service {}\''.format(IDA_PLUGIN_PATH, port))
args.append(payload)

execute_in_new_terminal(args)

while True:
try:
client = rpyc.connect('localhost', port, config={
# this is meant to disable the timeout
'sync_request_timeout': None,
'allow_all_attrs': True,
'allow_setattr': True,
})
break
except socket.error:
pass

return client


def launch_ida_in_service_mode(ida, payload, loader=None):
client = get_client(ida, payload, loader)
cprint('use `client.root` variable to access the remote object', 'cyan')
IPython.embed()
client.close()


@click.command()
@click.argument('ida', type=click.Path(exists=True))
@click.argument('payload', type=click.Path(exists=True))
@click.option('-l', '--loader', required=False)
def shell(ida, payload, loader):
launch_ida_in_service_mode(ida, payload, loader)


if __name__ == '__main__':
shell()
66 changes: 46 additions & 20 deletions fa/ida_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@
import re
import os

import rpyc
from rpyc import OneShotServer

sys.path.append('.') # noqa: E402

import hjson
import click

from ida_kernwin import Form
import ida_segment
import ida_kernwin
import ida_typeinf
import ida_struct
import ida_bytes
import idautils
import ida_auto
import ida_pro
import idaapi
import idc
Expand Down Expand Up @@ -258,7 +264,7 @@ def symbols(self, output_file_path=None):

results.update(ida_symbols)

except Exception as e:
except Exception:
traceback.print_exc()
finally:
ida_kernwin.hide_wait_box()
Expand All @@ -271,6 +277,7 @@ def export(self):
IDB.
:return: None
"""

class ExportForm(Form):
def __init__(self):
description = '''
Expand Down Expand Up @@ -347,7 +354,7 @@ def OnFormChange(self, fid):
.format(ifdef_name=ifdef_name))

if consts_ordinal is not None:
consts = re.findall('\s*(.+?) = (.+?),',
consts = re.findall('\\s*(.+?) = (.+?),',
idc.print_decls(
str(consts_ordinal), 0))
for k, v in consts:
Expand All @@ -365,8 +372,8 @@ def OnFormChange(self, fid):
structs_buf):
f.write(
'typedef {struct_type} {struct_name} {struct_name};\n'
.format(struct_type=struct_type,
struct_name=struct_name))
.format(struct_type=struct_type,
struct_name=struct_name))

structs_buf = structs_buf.replace('__fastcall', '')
f.write('\n')
Expand Down Expand Up @@ -402,6 +409,7 @@ def interactive_settings(self):
Show settings dialog
:return: None
"""

class SettingsForm(Form):
def __init__(self, signatures_root, use_template):
description = '''
Expand Down Expand Up @@ -455,6 +463,7 @@ def interactive_set_project(self):
Show set-project dialog
:return: None
"""

class SetProjectForm(Form):
def __init__(self, signatures_root, projects, current):
description = '''
Expand Down Expand Up @@ -515,6 +524,7 @@ def add_action(action):
:param action: action given as the `Action` namedtuple
:return: None
"""

class Handler(ida_kernwin.action_handler_t):
def __init__(self):
ida_kernwin.action_handler_t.__init__(self)
Expand Down Expand Up @@ -661,25 +671,38 @@ def install():


@click.command()
@click.argument('signatures_root', default='.')
@click.option('--project_name', default=None)
@click.option('--symbols-file', default=None)
def main(signatures_root, project_name, symbols_file=None):
plugin_main(signatures_root, project_name, symbols_file)
@click.option('-s', '--service', type=click.IntRange(1024, 65535), help='execute in rpyc service mode at given port')
def main(service):
plugin_main(service)


class FaService(rpyc.Service):
ida_segment = ida_segment
ida_kernwin = ida_kernwin
ida_typeinf = ida_typeinf
ida_struct = ida_struct
ida_bytes = ida_bytes
idautils = idautils
ida_auto = ida_auto
ida_pro = ida_pro
idaapi = idaapi
idc = idc

@staticmethod
def load_module(name, filename):
return fainterp.FaInterp.get_module(name, filename)


def plugin_main(signatures_root, project_name, symbols_file=None):
def plugin_main(service=None):
global fa_instance

fa_instance = IdaLoader()
fa_instance.set_input('ida')

if project_name is not None:
fa_instance.set_project(project_name)

load_ui()

IdaLoader.log(''' ---------------------------------
IdaLoader.log('''
---------------------------------
FA Loaded successfully
Quick usage:
Expand All @@ -689,12 +712,15 @@ def plugin_main(signatures_root, project_name, symbols_file=None):
fa_instance.set_symbol_template(status) # enable/disable template temp
signature
fa_instance.symbols() # searches for the symbols in the current project
---------------------------------''')
---------------------------------
''')

if symbols_file is not None:
fa_instance.set_signatures_root(signatures_root)
fa_instance.symbols(symbols_file)
ida_pro.qexit(0)
if service:
t = OneShotServer(FaService, port=service, protocol_config={
'allow_all_attrs': True,
'allow_setattr': True,
})
t.start()

# TODO: consider adding as autostart script
# install()
Expand All @@ -709,7 +735,7 @@ class FAIDAPlugIn(idaapi.plugin_t):
help = "Load FA in IDA Pro"

def init(self):
plugin_main('.', None, None)
plugin_main()
return idaapi.PLUGIN_KEEP

def run(self, args):
Expand Down
7 changes: 6 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ capstone
click
hjson
future
configparser
configparser
six
rpyc
click
ipython
termcolor
7 changes: 6 additions & 1 deletion requirements_testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ configparser
idalink
pytest
simpleelf
pyelftools
pyelftools
six
rpyc
click
ipython
termcolor
28 changes: 20 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
from pathlib import Path

from setuptools import setup

BASE_DIR = Path(__file__).parent.resolve(strict=True)


def parse_requirements():
reqs = []
with open(BASE_DIR / 'requirements.txt', 'r') as fd:
for line in fd.readlines():
line = line.strip()
if line:
reqs.append(line)
return reqs


setup(
name='fa',
version='0.2.2',
version='0.3.0',
description='FA Plugin',
author='DoronZ',
author_email='doron88@gmail.com',
url='https://github.com/doronz88/fa',
packages=['fa', 'fa.commands'],
package_dir={'fa': 'fa'},
package_data={'': ['*.png', '*'], },
include_package_data=True,
data_files=[(r'fa/res/icons', [r'fa/res/icons/create_sig.png',
r'fa/res/icons/export.png',
r'fa/res/icons/find.png',
Expand All @@ -18,11 +35,6 @@
r'fa/res/icons/suitcase.png']),
(r'fa/commands', ['fa/commands/alias']),
],
install_requires=['keystone-engine',
'capstone',
'click',
'hjson',
'future',
'configparser'],
python_requires='>=2.7'
install_requires=parse_requirements(),
python_requires='>=2.7',
)

0 comments on commit cfdceb6

Please sign in to comment.