From 76d44061ba22d2a4d665210af3f0538d48122593 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 00:57:43 +0300 Subject: [PATCH 01/10] fa_types: improve struct handling --- fa/fa_types.py | 26 +++++++------------ fa/signatures/test-project-ida/add_structs.py | 7 +++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/fa/fa_types.py b/fa/fa_types.py index 72a8f15..7da8315 100644 --- a/fa/fa_types.py +++ b/fa/fa_types.py @@ -50,33 +50,27 @@ def update_idb(self): class FaStruct(FaType): - Field = namedtuple('Field', ['name', 'type', 'size']) + Field = namedtuple('Field', ['name', 'type', 'offset']) def __init__(self, name): super(FaStruct, self).__init__(name) self._fields = [] - self._size = 0 - def add_field(self, name, type_, size=0, offset=0): - if (offset != 0) and (offset != self._size): - self.add_field('padd_{:x}'.format(self._size), - 'unsigned char[{}]'.format(offset - self._size), - offset - self._size) - - self._fields.append(self.Field(name, type_, size)) - self._size += size + def add_field(self, name, type_, offset=0xffffffff): + self._fields.append(self.Field(name, type_, offset)) def update_idb(self): sid = ida_struct.get_struc_id(self._name) - if sid != -1: - sptr = ida_struct.get_struc(sid) - ida_struct.del_struc(sptr) - - sid = ida_struct.add_struc(idc.BADADDR, self._name, 0) sptr = ida_struct.get_struc(sid) + if sid == idc.BADADDR: + sid = ida_struct.add_struc(idc.BADADDR, self._name, 0) + sptr = ida_struct.get_struc(sid) + else: + ida_struct.del_struc_members(sptr, 0, 0xffffffff) + for f in self._fields: - ida_struct.add_struc_member(sptr, f.name, idc.BADADDR, + ida_struct.add_struc_member(sptr, f.name, f.offset, (idc.FF_BYTE | idc.FF_DATA) & 0xFFFFFFFF, None, 1) diff --git a/fa/signatures/test-project-ida/add_structs.py b/fa/signatures/test-project-ida/add_structs.py index a63b366..4f34de2 100644 --- a/fa/signatures/test-project-ida/add_structs.py +++ b/fa/signatures/test-project-ida/add_structs.py @@ -11,6 +11,9 @@ def run(**kwargs): foo_e.update_idb() special_struct_t = fa_types.FaStruct('special_struct_t') - special_struct_t.add_field('member1', 'const char *', size=4) - special_struct_t.add_field('member2', 'const char *', size=4, offset=0x20) + special_struct_t.add_field('member1', 'const char *') + special_struct_t.add_field('member2', 'const char *', offset=0x20) + special_struct_t.add_field('member3', 'char', offset=0x60) + special_struct_t.add_field('member4', 'char', offset=0x61) + special_struct_t.add_field('member5', 'const char *', offset=0x80) special_struct_t.update_idb() From 5701bcf1ce22e904c1b8106a4f1cf5b4205dfac9 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 00:58:23 +0300 Subject: [PATCH 02/10] fainterp: handle non-run python scripts --- fa/fainterp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fa/fainterp.py b/fa/fainterp.py index bd988ce..b4e136d 100644 --- a/fa/fainterp.py +++ b/fa/fainterp.py @@ -412,7 +412,10 @@ def get_python_symbols(self, file_name=None): name = os.path.splitext(filename)[0] filename = os.path.join(project_root, filename) m = FaInterp.get_module(name, filename) - m.run(interpreter=self) + if not hasattr(m, 'run'): + self.log('skipping: {}'.format(filename)) + else: + m.run(interpreter=self) def get_json_signatures(self, symbol_name=None): """ From b5996039c3704e8b8184a118af9f4dd27c4ea573 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:01:00 +0300 Subject: [PATCH 03/10] commands: set-name: add set_name api function --- fa/commands/set_name.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fa/commands/set_name.py b/fa/commands/set_name.py index 55161d7..34852ed 100644 --- a/fa/commands/set_name.py +++ b/fa/commands/set_name.py @@ -13,7 +13,11 @@ def get_parser(): return p -def run(segments, args, addresses, interpreter=None, **kwargs): +def set_name(addresses, name, interpreter): for ea in addresses: - interpreter.set_symbol(args.name, ea) + interpreter.set_symbol(name, ea) return addresses + + +def run(segments, args, addresses, interpreter=None, **kwargs): + return set_name(addresses, args.name, interpreter) From 56b6f2f270f772aab763612bdfc4bc3ba6768814 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:02:10 +0300 Subject: [PATCH 04/10] fainterp: wrap logged objects with str() --- fa/fainterp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fa/fainterp.py b/fa/fainterp.py index b4e136d..e878e52 100644 --- a/fa/fainterp.py +++ b/fa/fainterp.py @@ -198,7 +198,7 @@ def log(message): :param message: :return: """ - for line in message.splitlines(): + for line in str(message).splitlines(): print('FA> {}'.format(line)) @abstractmethod From 5dd80fc0d00594db201f45584df2c5de6cb80362 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:03:35 +0300 Subject: [PATCH 05/10] fainterp: allow running of python files inside directories within the project --- fa/fainterp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fa/fainterp.py b/fa/fainterp.py index e878e52..a44d7ce 100644 --- a/fa/fainterp.py +++ b/fa/fainterp.py @@ -410,7 +410,7 @@ def get_python_symbols(self, file_name=None): if not file_name or file_name == filename: name = os.path.splitext(filename)[0] - filename = os.path.join(project_root, filename) + filename = os.path.join(root, filename) m = FaInterp.get_module(name, filename) if not hasattr(m, 'run'): self.log('skipping: {}'.format(filename)) From 879691671aae47eb64a243af2a097e49b594eb52 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:09:02 +0300 Subject: [PATCH 06/10] commands: add find command --- fa/commands/find.py | 16 ++++++++++++++++ fa/signatures/test-project-elf/test-basic.sig | 4 ++++ fa/signatures/test-project-elf/test_find.sig | 8 ++++++++ fa/signatures/test-project-ida/test-basic.sig | 4 ++++ fa/signatures/test-project-ida/test_find.sig | 8 ++++++++ tests/test_commands/test_elf.py | 1 + tests/test_commands/test_idalink.py | 1 + 7 files changed, 42 insertions(+) create mode 100644 fa/commands/find.py create mode 100644 fa/signatures/test-project-elf/test_find.sig create mode 100644 fa/signatures/test-project-ida/test_find.sig diff --git a/fa/commands/find.py b/fa/commands/find.py new file mode 100644 index 0000000..c0351e0 --- /dev/null +++ b/fa/commands/find.py @@ -0,0 +1,16 @@ +from fa import utils + + +def get_parser(): + p = utils.ArgumentParserNoExit('find', + description='find another symbol defined ' + 'in other SIG files') + p.add_argument('name', help='symbol name') + return p + + +def run(segments, args, addresses, interpreter=None, **kwargs): + interpreter.find(args.name) + + # return an empty result-set + return [] diff --git a/fa/signatures/test-project-elf/test-basic.sig b/fa/signatures/test-project-elf/test-basic.sig index 4f94a54..e44cf86 100644 --- a/fa/signatures/test-project-elf/test-basic.sig +++ b/fa/signatures/test-project-elf/test-basic.sig @@ -87,5 +87,9 @@ find-str '3DUfw' set-name test_find_str + + clear + + find test_find ] } diff --git a/fa/signatures/test-project-elf/test_find.sig b/fa/signatures/test-project-elf/test_find.sig new file mode 100644 index 0000000..f46fecd --- /dev/null +++ b/fa/signatures/test-project-elf/test_find.sig @@ -0,0 +1,8 @@ +{ + "type": "function", + "name": "test_find", + "instructions": [ + add 76 + set-name test_find + ] +} diff --git a/fa/signatures/test-project-ida/test-basic.sig b/fa/signatures/test-project-ida/test-basic.sig index 4f94a54..e44cf86 100644 --- a/fa/signatures/test-project-ida/test-basic.sig +++ b/fa/signatures/test-project-ida/test-basic.sig @@ -87,5 +87,9 @@ find-str '3DUfw' set-name test_find_str + + clear + + find test_find ] } diff --git a/fa/signatures/test-project-ida/test_find.sig b/fa/signatures/test-project-ida/test_find.sig new file mode 100644 index 0000000..f46fecd --- /dev/null +++ b/fa/signatures/test-project-ida/test_find.sig @@ -0,0 +1,8 @@ +{ + "type": "function", + "name": "test_find", + "instructions": [ + add 76 + set-name test_find + ] +} diff --git a/tests/test_commands/test_elf.py b/tests/test_commands/test_elf.py index 4cda47d..085bf48 100644 --- a/tests/test_commands/test_elf.py +++ b/tests/test_commands/test_elf.py @@ -30,3 +30,4 @@ def test_elf_symbols(sample_elf): assert symbols['test_append'] == 2 assert symbols['test_find_bytes'] == 0x1240 assert symbols['test_find_str'] == 0x1242 + assert symbols['test_find'] == 76 diff --git a/tests/test_commands/test_idalink.py b/tests/test_commands/test_idalink.py index 5872c14..0e8c5c0 100644 --- a/tests/test_commands/test_idalink.py +++ b/tests/test_commands/test_idalink.py @@ -87,6 +87,7 @@ def test_ida_symbols(ida, sample_elf): assert symbols['test_append'] == 2 assert symbols['test_find_bytes'] == 0x1240 assert symbols['test_find_str'] == 0x1242 + assert symbols['test_find'] == 76 # from test-ida-context assert symbols['test_find_bytes_ida'] == 0x1240 From 513f19df803ddf978a8bf0ca789697af9334a85d Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:09:06 +0300 Subject: [PATCH 07/10] commands: set-name: add set_name api function --- commands.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/commands.md b/commands.md index 2084fc1..6601e3d 100644 --- a/commands.md +++ b/commands.md @@ -9,6 +9,7 @@ Below is the list of available commands: - [back-to-checkpoint](#back-to-checkpoint) - [checkpoint](#checkpoint) - [clear](#clear) +- [find](#find) - [find-bytes](#find-bytes) - [find-bytes-ida](#find-bytes-ida) - [find-immediate](#find-immediate) @@ -195,6 +196,18 @@ EXAMPLE: -> clear results = [] +optional arguments: + -h, --help show this help message and exit +``` +## find +``` +usage: find [-h] name + +find another symbol defined in other SIG files + +positional arguments: + name symbol name + optional arguments: -h, --help show this help message and exit ``` From 45adc4ee8dadb6c0eff7640e692f9cd596916e9b Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:11:53 +0300 Subject: [PATCH 08/10] docs: update README --- README.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 587d671..2185543 100644 --- a/README.md +++ b/README.md @@ -258,18 +258,18 @@ To view the list of available commands, [view the list below](#available-command ```python from fa.commands.find_str import find_str +from fa.commands.set_name import set_name from fa import context def run(**kwargs): # throw an exception if not running within ida context context.verify_ida('script-name') + interp = kwargs['interpreter'] # locate the global string - resultset = list(find_str('hello world', null_terminated=True) - - if len(resultset) == 1: - interp.set_symbol('g_hello_world', resultset[0]) + resultset = set_name(find_str('hello world', null_terminated=True), + 'g_hello_world', interp) ``` #### Python script to automate SIG files interpreter @@ -284,16 +284,13 @@ set-name '{function_name}' ''' def run(**kwargs): - results = {} interp = kwargs['interpreter'] for function_name in ['func1', 'func2', 'func3']: instructions = TEMPLATE.format(unique_string=function_name, function_name=function_name).split('\n') - results[function_name] = interp.find_from_instructions_list(instructions) - - return results + interp.find_from_instructions_list(instructions) ``` #### Python script to dynamically add structs @@ -332,8 +329,6 @@ def run(**kwargs): # the set_type can receive either a string, FaStruct # or FaEnum :-) set_type(ea, special_struct_t) - - return {} ``` ### Aliases From 72bfe61f9127107eb1ca31798cc1b519c81429ec Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:16:18 +0300 Subject: [PATCH 09/10] fainterp: interpreter is now passed to python scripts instead of kwargs --- README.md | 20 ++++++++----------- fa/fainterp.py | 2 +- fa/signatures/test-project-ida/add_structs.py | 2 +- fa/signatures/test-project-ida/explore.py | 5 ++--- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2185543..19463a7 100644 --- a/README.md +++ b/README.md @@ -98,9 +98,9 @@ Confused? That's alright :grinning:. [Just look at the examples below](#examples User may also use his own python script files to perform the search. Just create a new `.py` file in your project -directory and implement the `run(**kwargs)` method. Also, the project's -path is appended to python's `sys.path` so you may import -your scripts from one another. +directory and implement the `run(interpreter)` method. +Also, the project's path is appended to python's `sys.path` +so you may import your scripts from one another. To view the list of available commands, [view the list below](#available-commands) @@ -261,15 +261,13 @@ from fa.commands.find_str import find_str from fa.commands.set_name import set_name from fa import context -def run(**kwargs): +def run(interpreter): # throw an exception if not running within ida context context.verify_ida('script-name') - interp = kwargs['interpreter'] - # locate the global string - resultset = set_name(find_str('hello world', null_terminated=True), - 'g_hello_world', interp) + set_name(find_str('hello world', null_terminated=True), + 'g_hello_world', interpreter) ``` #### Python script to automate SIG files interpreter @@ -283,14 +281,12 @@ unique set-name '{function_name}' ''' -def run(**kwargs): - interp = kwargs['interpreter'] - +def run(interpreter): for function_name in ['func1', 'func2', 'func3']: instructions = TEMPLATE.format(unique_string=function_name, function_name=function_name).split('\n') - interp.find_from_instructions_list(instructions) + interpreter.find_from_instructions_list(instructions) ``` #### Python script to dynamically add structs diff --git a/fa/fainterp.py b/fa/fainterp.py index a44d7ce..1517dbf 100644 --- a/fa/fainterp.py +++ b/fa/fainterp.py @@ -415,7 +415,7 @@ def get_python_symbols(self, file_name=None): if not hasattr(m, 'run'): self.log('skipping: {}'.format(filename)) else: - m.run(interpreter=self) + m.run(self) def get_json_signatures(self, symbol_name=None): """ diff --git a/fa/signatures/test-project-ida/add_structs.py b/fa/signatures/test-project-ida/add_structs.py index 4f34de2..35b29d2 100644 --- a/fa/signatures/test-project-ida/add_structs.py +++ b/fa/signatures/test-project-ida/add_structs.py @@ -1,7 +1,7 @@ from fa import fa_types -def run(**kwargs): +def run(interpreter): fa_types.add_const('CONST7', 7) fa_types.add_const('CONST8', 8) diff --git a/fa/signatures/test-project-ida/explore.py b/fa/signatures/test-project-ida/explore.py index 515f905..d0660c5 100644 --- a/fa/signatures/test-project-ida/explore.py +++ b/fa/signatures/test-project-ida/explore.py @@ -4,6 +4,5 @@ ''' -def run(**kwargs): - interp = kwargs['interpreter'] - interp.find_from_instructions_list(TEMPLATE.splitlines()) +def run(interpreter): + interpreter.find_from_instructions_list(TEMPLATE.splitlines()) From fae0f548977f454e685f3bae645c09a84c07bcc8 Mon Sep 17 00:00:00 2001 From: DoronZ Date: Fri, 24 Jul 2020 01:18:48 +0300 Subject: [PATCH 10/10] update to version 0.1.4 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 763f110..b3f4447 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='fa', - version='0.1.3', + version='0.1.4', description='FA Plugin', author='DoronZ', author_email='doron88@gmail.com',