fa 0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. fa-0.1.0/PKG-INFO +11 -0
  2. fa-0.1.0/README.md +418 -0
  3. fa-0.1.0/fa/__init__.py +0 -0
  4. fa-0.1.0/fa/commands/__init__.py +0 -0
  5. fa-0.1.0/fa/commands/add_offset_range.py +33 -0
  6. fa-0.1.0/fa/commands/alias +8 -0
  7. fa-0.1.0/fa/commands/align.py +26 -0
  8. fa-0.1.0/fa/commands/back.py +29 -0
  9. fa-0.1.0/fa/commands/back_to_checkpoint.py +28 -0
  10. fa-0.1.0/fa/commands/checkpoint.py +29 -0
  11. fa-0.1.0/fa/commands/clear.py +21 -0
  12. fa-0.1.0/fa/commands/find_bytes.py +46 -0
  13. fa-0.1.0/fa/commands/find_bytes_ida.py +47 -0
  14. fa-0.1.0/fa/commands/find_immediate.py +54 -0
  15. fa-0.1.0/fa/commands/find_str.py +42 -0
  16. fa-0.1.0/fa/commands/function_end.py +42 -0
  17. fa-0.1.0/fa/commands/function_lines.py +48 -0
  18. fa-0.1.0/fa/commands/function_start.py +48 -0
  19. fa-0.1.0/fa/commands/goto_ref.py +57 -0
  20. fa-0.1.0/fa/commands/keystone_find_opcodes.py +55 -0
  21. fa-0.1.0/fa/commands/keystone_verify_opcodes.py +57 -0
  22. fa-0.1.0/fa/commands/locate.py +38 -0
  23. fa-0.1.0/fa/commands/make_code.py +23 -0
  24. fa-0.1.0/fa/commands/make_comment.py +43 -0
  25. fa-0.1.0/fa/commands/make_function.py +23 -0
  26. fa-0.1.0/fa/commands/make_literal.py +23 -0
  27. fa-0.1.0/fa/commands/make_unknown.py +23 -0
  28. fa-0.1.0/fa/commands/max_xrefs.py +32 -0
  29. fa-0.1.0/fa/commands/min_xrefs.py +32 -0
  30. fa-0.1.0/fa/commands/most_common.py +28 -0
  31. fa-0.1.0/fa/commands/offset.py +27 -0
  32. fa-0.1.0/fa/commands/print.py +17 -0
  33. fa-0.1.0/fa/commands/run.py +15 -0
  34. fa-0.1.0/fa/commands/set_name.py +25 -0
  35. fa-0.1.0/fa/commands/set_type.py +37 -0
  36. fa-0.1.0/fa/commands/single.py +29 -0
  37. fa-0.1.0/fa/commands/sort.py +26 -0
  38. fa-0.1.0/fa/commands/trace.py +18 -0
  39. fa-0.1.0/fa/commands/unique.py +30 -0
  40. fa-0.1.0/fa/commands/verify_aligned.py +26 -0
  41. fa-0.1.0/fa/commands/verify_bytes.py +52 -0
  42. fa-0.1.0/fa/commands/verify_name.py +23 -0
  43. fa-0.1.0/fa/commands/verify_operand.py +77 -0
  44. fa-0.1.0/fa/commands/verify_ref.py +55 -0
  45. fa-0.1.0/fa/commands/verify_segment.py +43 -0
  46. fa-0.1.0/fa/commands/verify_str.py +33 -0
  47. fa-0.1.0/fa/commands/xref.py +25 -0
  48. fa-0.1.0/fa/commands/xrefs_to.py +53 -0
  49. fa-0.1.0/fa/context.py +51 -0
  50. fa-0.1.0/fa/fa_types.py +93 -0
  51. fa-0.1.0/fa/fainterp.py +483 -0
  52. fa-0.1.0/fa/ida_plugin.py +684 -0
  53. fa-0.1.0/fa/res/icons/create_sig.png +0 -0
  54. fa-0.1.0/fa/res/icons/export.png +0 -0
  55. fa-0.1.0/fa/res/icons/find.png +0 -0
  56. fa-0.1.0/fa/res/icons/find_all.png +0 -0
  57. fa-0.1.0/fa/res/icons/save.png +0 -0
  58. fa-0.1.0/fa/res/icons/settings.png +0 -0
  59. fa-0.1.0/fa/res/icons/suitcase.png +0 -0
  60. fa-0.1.0/fa/utils.py +94 -0
  61. fa-0.1.0/fa.egg-info/PKG-INFO +11 -0
  62. fa-0.1.0/fa.egg-info/SOURCES.txt +65 -0
  63. fa-0.1.0/fa.egg-info/dependency_links.txt +1 -0
  64. fa-0.1.0/fa.egg-info/requires.txt +6 -0
  65. fa-0.1.0/fa.egg-info/top_level.txt +1 -0
  66. fa-0.1.0/setup.cfg +4 -0
  67. fa-0.1.0/setup.py +28 -0
fa-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 1.2
2
+ Name: fa
3
+ Version: 0.1.0
4
+ Summary: FA Plugin
5
+ Home-page: https://github.com/doronz88/fa
6
+ Author: DoronZ
7
+ Author-email: doron88@gmail.com
8
+ License: UNKNOWN
9
+ Description: UNKNOWN
10
+ Platform: UNKNOWN
11
+ Requires-Python: >=2.7
fa-0.1.0/README.md ADDED
@@ -0,0 +1,418 @@
1
+ ![Python application](https://github.com/doronz88/fa/workflows/Python%20application/badge.svg)
2
+
3
+ # FA
4
+
5
+ ## What is it?
6
+
7
+ FA stands for Firmware Analysis and intended **For Humans**.
8
+
9
+ FA allows one to easily perform code exploration, symbol searching and
10
+ other functionality with ease.
11
+
12
+ Usually such tasks would require you to understand complicated APIs,
13
+ write machine-dependant code and perform other tedious tasks.
14
+ FA is meant to replace the steps one usually performs like a robot
15
+ (find X string, goto xref, find the next call function, ...) in
16
+ a much friendlier and maintainable manner.
17
+
18
+ The current codebase is very IDA-plugin-oriented. In the future I'll
19
+ consider adding compatibility for other disassemblers such as:
20
+ Ghidra, Radare and etc...
21
+
22
+
23
+ Pull Requests are of course more than welcome :smirk:.
24
+
25
+ ## Requirements
26
+
27
+ Supported IDA 7.x.
28
+
29
+ In your IDA's python directory, run:
30
+
31
+ ```sh
32
+ python -m pip install -r requirements.txt
33
+ ```
34
+
35
+ And for testing:
36
+ ```sh
37
+ python -m pip install -r requirements_testing.txt
38
+ ```
39
+
40
+ ## I wanna start using, but where do I start?
41
+
42
+ Before using, you should understand the terminology for:
43
+ Projects, SIG files and Loaders.
44
+
45
+ So, grab a cup of coffee, listen to some [nice music](https://www.youtube.com/watch?v=5rrIx7yrxwQ), and please devote
46
+ a few minutes of your time into reading this README.
47
+
48
+ ### Projects
49
+
50
+ The "project" is kind of the namespace for different signatures.
51
+ For example, either: linux, linux_x86, linux_arm etc... are good
52
+ project names that can be specified if you are working on either
53
+ platforms.
54
+
55
+ By dividing the signatures into such projects, Windows symbols for
56
+ example won't be searched for Linux projects, which will result
57
+ in a better directory organization layout, better performance and
58
+ less rate for false-positives.
59
+
60
+ The signatures are located by default in the `signatures` directory.
61
+ If one wishes to use a different location, you may create `config.ini`
62
+ at FA's root with the following contents:
63
+
64
+ ```ini
65
+ [global]
66
+ signatures_root = /a/b/c
67
+ ```
68
+
69
+ ### SIG format
70
+
71
+ The SIG format is a core feature of FA regarding symbol searching. Each
72
+ SIG file is residing within the project directory and is automatically searched
73
+ when requested to generate the project's symbol list.
74
+
75
+ The format is Hjson-based and is used to describe what you,
76
+ **as a human**, would do in order to perform the given task (symbol searching
77
+ or binary exploration).
78
+
79
+ SIG syntax (single):
80
+ ```hjson
81
+ {
82
+ type: function/global/number # doesn't really have meaning
83
+ name: name
84
+ instructions : [
85
+ # Available commands are listed below
86
+ command1
87
+ command2
88
+ ]
89
+ }
90
+ ```
91
+
92
+ Each line in the `instructions` list behaves like a shell
93
+ command-line that gets the previous results as the input
94
+ and outputs the next results
95
+ to the next line.
96
+
97
+ Confused? That's alright :grinning:. [Just look at the examples below](#examples)
98
+
99
+ User may also use his own python script files to perform
100
+ the search. Just create a new `.py` file in your project
101
+ directory and implement the `run(**kwargs)` method. Also, the project's
102
+ path is appended to python's `sys.path` so you may import
103
+ your scripts from one another.
104
+
105
+ To view the list of available commands, [view the list below](#available-commands)
106
+
107
+ ### Examples
108
+
109
+ #### Finding a global struct
110
+
111
+ ```hjson
112
+ {
113
+ type: global,
114
+ name: g_awsome_global,
115
+ instructions: [
116
+ # find the byte sequence '11 22 33 44'
117
+ find-bytes --or '11 22 33 44'
118
+
119
+ # advance offset by 20
120
+ offset 20
121
+
122
+ # verify the current bytes are 'aa bb cc dd'
123
+ verify-bytes 'aa bb cc dd'
124
+
125
+ # go back by 20 bytes offset
126
+ offset -20
127
+
128
+ # set global name
129
+ set-name g_awsome_global
130
+ ]
131
+ }
132
+ ```
133
+
134
+ #### Find function by reference to string
135
+
136
+ ```hjson
137
+ {
138
+ type: function
139
+ name: free
140
+ instructions: [
141
+ # search the string "free"
142
+ find-str --or 'free' --null-terminated
143
+
144
+ # goto xref
145
+ xref
146
+
147
+ # goto function's prolog
148
+ function-start
149
+
150
+ # reduce to the singletone with most xrefs to
151
+ max-xrefs
152
+
153
+ # set name and type
154
+ set-name free
155
+ set-type 'void free(void *block)'
156
+ ]
157
+ }
158
+ ```
159
+
160
+ #### Performing code exploration
161
+
162
+ ```hjson
163
+ {
164
+ type: function-list
165
+ name: arm-explorer
166
+ instructions: [
167
+ # search for some potential function prologs
168
+ arm-find-all 'push {r4, lr}'
169
+ arm-find-all 'push {r4, r5, lr}'
170
+ thumb-find-all 'push {r4, lr}'
171
+ thumb-find-all 'push {r4, r5, lr}'
172
+
173
+ # convert into functions
174
+ make-function
175
+ ]
176
+ }
177
+ ```
178
+
179
+ #### Performing string exploration
180
+
181
+ ```hjson
182
+ {
183
+ type: explorer
184
+ name: arm-string-explorer
185
+ instructions: [
186
+ # goto printf
187
+ locate printf
188
+
189
+ # iterate every xref
190
+ xref
191
+
192
+ # and for each, go word-word backwards
193
+ add-offset-range 0 -40 -4
194
+
195
+ # if ldr to r0
196
+ verify-operand ldr --op0 r0
197
+
198
+ # go to the global string
199
+ goto-ref --data
200
+
201
+ # and make it literal
202
+ make-literal
203
+ ]
204
+ }
205
+ ```
206
+
207
+ #### Finding several functions in a row
208
+
209
+ ```hjson
210
+ {
211
+ type: function
212
+ name: cool_functions
213
+ instructions: [
214
+ # find string
215
+ find-str --or 'init_stuff' --null-terminated
216
+
217
+ # goto to xref
218
+ xref
219
+
220
+ # goto function start
221
+ function-start
222
+
223
+ # verify only one single result
224
+ unique
225
+
226
+ # iterating every 4-byte opcode
227
+ add-offset-range 0 80 4
228
+
229
+ # if mnemonic is bl
230
+ verify-operand bl
231
+
232
+ # sort results
233
+ sort
234
+
235
+ # mark resultset checkpoint
236
+ checkpoint BLs
237
+
238
+ # set first bl to malloc function
239
+ single 0
240
+ goto-ref --code
241
+ set-name malloc
242
+ set-type 'void *malloc(unsigned int size)'
243
+
244
+ # go back to the results from 4 commands ago
245
+ # (the sort results)
246
+ back-to-checkpoint BLs
247
+
248
+ # rename next symbol :)
249
+ single 1
250
+ goto-ref --code
251
+ set-name free
252
+ set-type 'void free(void *block)'
253
+ ]
254
+ }
255
+ ```
256
+
257
+ #### Python script to find a list of symbols
258
+
259
+ ```python
260
+ from fa.commands.find_str import find_str
261
+ from fa.commands.set_name import set_name
262
+ from fa.commands.unique import unique
263
+ from fa import context
264
+
265
+ def run(**kwargs):
266
+ # throw an exception if not running within ida context
267
+ context.verify_ida('script-name')
268
+
269
+ # locate the global string, verify it's unique, and set it's
270
+ # name within the idb
271
+ results = set_name(unique(find_str('hello world', null_terminated=True)),
272
+ 'g_hello_world_string')
273
+
274
+ if len(results) != 1:
275
+ # no results
276
+ return {}
277
+
278
+ # return a dictionary of the found symbols
279
+ return {'g_hello_world_string': results[0]}
280
+ ```
281
+
282
+ #### Python script to automate SIG files interpreter
283
+
284
+ ```python
285
+ TEMPLATE = '''
286
+ find-str --or '{unique_string}'
287
+ xref
288
+ function-start
289
+ unique
290
+ set-name '{function_name}'
291
+ '''
292
+
293
+ def run(**kwargs):
294
+ results = {}
295
+ interp = kwargs['interpreter']
296
+
297
+ for function_name in ['func1', 'func2', 'func3']:
298
+ instructions = TEMPLATE.format(unique_string=function_name,
299
+ function_name=function_name).split('\n')
300
+
301
+ results[function_name] = interp.find_from_instructions_list(instructions)
302
+
303
+ return results
304
+ ```
305
+
306
+ #### Python script to dynamically add structs
307
+
308
+ ```python
309
+ from fa.commands.set_type import set_type
310
+ from fa import fa_types
311
+
312
+ TEMPLATE = '''
313
+ find-str --or '{unique_string}'
314
+ xref
315
+ '''
316
+
317
+ def run(**kwargs):
318
+ interp = kwargs['interpreter']
319
+
320
+ fa_types.add_const('CONST7', 7)
321
+ fa_types.add_const('CONST8', 8)
322
+
323
+ foo_e = fa_types.FaEnum('foo_e')
324
+ foo_e.add_value('val2', 2)
325
+ foo_e.add_value('val1', 1)
326
+ foo_e.update_idb()
327
+
328
+ special_struct_t = fa_types.FaStruct('special_struct_t')
329
+ special_struct_t.add_field('member1', 'const char *', size=4)
330
+ special_struct_t.add_field('member2', 'const char *', size=4, offset=0x20)
331
+ special_struct_t.update_idb()
332
+
333
+ for function_name in ['unique_magic1', 'unique_magic2']:
334
+ instructions = TEMPLATE.format(unique_string=function_name,
335
+ function_name=function_name).split('\n')
336
+
337
+ results = interp.find_from_instructions_list(instructions)
338
+ for ea in results:
339
+ # the set_type can receive either a string, FaStruct
340
+ # or FaEnum :-)
341
+ set_type(ea, special_struct_t)
342
+
343
+ return {}
344
+ ```
345
+
346
+ ### Aliases
347
+
348
+ Each command can be "alias"ed using the file
349
+ found in `fa/commands/alias` or in `<project_root>/alias`
350
+
351
+ Syntax for each line is as follows: `alias_command = command`
352
+ For example:
353
+ ```
354
+ ppc32-verify = keystone-verify-opcodes --bele KS_ARCH_PPC KS_MODE_PPC32
355
+ ```
356
+
357
+ Project aliases have higher priority.
358
+
359
+ ### Loaders
360
+
361
+ Loaders are the entry point into running FA.
362
+ In the future we'll possibly add Ghidra and other tools.
363
+
364
+ Please first install the package as follows:
365
+
366
+ Clone the repository and install locally:
367
+
368
+ ```sh
369
+ # clone
370
+ git clone git@github.com:doronz88/fa.git
371
+ cd fa
372
+
373
+ # install
374
+ python -m pip install -e .
375
+ ```
376
+
377
+ #### IDA
378
+
379
+ Within IDA Python run:
380
+
381
+ ```python
382
+ from fa import ida_plugin
383
+ ida_plugin.install()
384
+ ```
385
+
386
+ You should get a nice prompt inside the output window welcoming you
387
+ into using FA. Also, a quick usage guide will also be printed so you
388
+ don't have to memorize everything.
389
+
390
+ Also, an additional `FA Toolbar` will be added with quick functions that
391
+ are also available under the newly created `FA` menu.
392
+
393
+ ![FA Menu](https://github.com/doronz88/fa/raw/master/fa/res/screenshots/menu.png "FA Menu")
394
+
395
+ A QuickStart Tip:
396
+
397
+ `Ctrl+6` to select your project, then `Ctrl+7` to find all its defined symbols.
398
+
399
+
400
+ You can also run IDA in script mode just to extract symbols using:
401
+
402
+ ```sh
403
+ ida -S"fa/ida_plugin.py <signatures-root> --project-name <project-name> --symbols-file=/tmp/symbols.txt" foo.idb
404
+ ```
405
+
406
+
407
+ #### ELF
408
+
409
+ In order to use FA on a RAW ELF file, simply use the following command-line:
410
+
411
+ ```sh
412
+ python elf_loader.py <elf-file> <signatures_root> <project>
413
+ ```
414
+
415
+ ### Available commands
416
+
417
+ See [commands.md](commands.md)
418
+
File without changes
File without changes
@@ -0,0 +1,33 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from fa import utils
3
+
4
+
5
+ DESCRIPTION = '''adds a python-range to resultset
6
+
7
+ EXAMPLE:
8
+ result = [0, 0x200]
9
+ -> add-offset-range 0 4 8
10
+ result = [0, 4, 8, 0x200, 0x204, 0x208]
11
+ '''
12
+
13
+
14
+ def get_parser():
15
+ p = utils.ArgumentParserNoExit('add-offset-range',
16
+ description=DESCRIPTION,
17
+ formatter_class=RawTextHelpFormatter)
18
+ p.add_argument('start', type=int)
19
+ p.add_argument('end', type=int)
20
+ p.add_argument('step', type=int)
21
+ return p
22
+
23
+
24
+ @utils.yield_unique
25
+ def add_offset_range(addresses, start, end, step):
26
+ for ea in addresses:
27
+ for i in range(start, end, step):
28
+ yield ea + i
29
+
30
+
31
+ def run(segments, args, addresses, interpreter=None, **kwargs):
32
+ gen = add_offset_range(addresses, args.start, args.end, args.step)
33
+ return list(gen)
@@ -0,0 +1,8 @@
1
+ ppc32-big-find-all = keystone-find-opcodes --or KS_ARCH_PPC KS_MODE_BIG_ENDIAN|KS_MODE_PPC32
2
+ ppc32-find-all = keystone-find-opcodes --bele --or KS_ARCH_PPC KS_MODE_PPC32
3
+ ppc32-big-verify = keystone-verify-opcodes KS_ARCH_PPC KS_MODE_BIG_ENDIAN|KS_MODE_PPC32
4
+ ppc32-verify = keystone-verify-opcodes --bele KS_ARCH_PPC KS_MODE_PPC32
5
+ arm-find-all = keystone-find-opcodes --bele --or KS_ARCH_ARM KS_MODE_ARM
6
+ thumb-find-all = keystone-find-opcodes --bele --or KS_ARCH_ARM KS_MODE_THUMB
7
+ arm-verify = keystone-verify-opcodes --bele KS_ARCH_ARM KS_MODE_ARM
8
+ find-imm = find-immediate
@@ -0,0 +1,26 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from fa import utils
3
+
4
+ DESCRIPTION = '''align results to given base (round-up)
5
+
6
+ EXAMPLE:
7
+ results = [0, 2, 4, 6, 8]
8
+ -> align 4
9
+ results = [0, 4, 4, 8, 8]
10
+ '''
11
+
12
+
13
+ def get_parser():
14
+ p = utils.ArgumentParserNoExit('align',
15
+ description=DESCRIPTION,
16
+ formatter_class=RawTextHelpFormatter)
17
+ p.add_argument('value', type=int)
18
+ return p
19
+
20
+
21
+ def align(addresses, value):
22
+ return [((ea + (value - 1)) // value) * value for ea in addresses]
23
+
24
+
25
+ def run(segments, args, addresses, interpreter=None, **kwargs):
26
+ return list(align(addresses, args.value))
@@ -0,0 +1,29 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from fa import utils
3
+
4
+
5
+ DESCRIPTION = '''go back to previous result-set
6
+
7
+ EXAMPLE:
8
+ find-bytes --or 01 02 03 04
9
+ results = [0, 0x100, 0x200]
10
+
11
+ find-bytes --or 05 06 07 08
12
+ results = [0, 0x100, 0x200, 0x300, 0x400]
13
+
14
+ -> back -3
15
+ results = [0, 0x100, 0x200]
16
+ '''
17
+
18
+
19
+ def get_parser():
20
+ p = utils.ArgumentParserNoExit('back',
21
+ description=DESCRIPTION,
22
+ formatter_class=RawTextHelpFormatter)
23
+ p.add_argument('amount', type=int,
24
+ help='amount of command results to go back by')
25
+ return p
26
+
27
+
28
+ def run(segments, args, addresses, interpreter=None, **kwargs):
29
+ return interpreter.history[-args.amount]
@@ -0,0 +1,28 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from fa import utils
3
+
4
+ DESCRIPTION = '''go back to previous result-set saved by 'checkpoint' command.
5
+
6
+ EXAMPLE:
7
+ results = [0, 4, 8]
8
+ checkpoint foo
9
+
10
+ find-bytes --or 12345678
11
+ results = [0, 4, 8, 10, 20]
12
+
13
+ -> back-to-checkpoint foo
14
+ results = [0, 4, 8]
15
+ '''
16
+
17
+
18
+ def get_parser():
19
+ p = utils.ArgumentParserNoExit('back-to-checkpoint',
20
+ description=DESCRIPTION,
21
+ formatter_class=RawTextHelpFormatter)
22
+ p.add_argument('name', help='name of checkpoint in history to go back '
23
+ 'to')
24
+ return p
25
+
26
+
27
+ def run(segments, args, addresses, interpreter=None, **kwargs):
28
+ return interpreter.checkpoints[args.name]
@@ -0,0 +1,29 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from fa import utils
3
+
4
+ DESCRIPTION = '''save current result-set as a checkpoint.
5
+ You can later restore the result-set using 'back-to-checkpoint'
6
+
7
+ EXAMPLE:
8
+ results = [0, 4, 8]
9
+ -> checkpoint foo
10
+
11
+ find-bytes --or 12345678
12
+ results = [0, 4, 8, 10, 20]
13
+
14
+ back-to-checkpoint foo
15
+ results = [0, 4, 8]
16
+ '''
17
+
18
+
19
+ def get_parser():
20
+ p = utils.ArgumentParserNoExit('checkpoint',
21
+ description=DESCRIPTION,
22
+ formatter_class=RawTextHelpFormatter)
23
+ p.add_argument('name', help='name of checkpoint to use')
24
+ return p
25
+
26
+
27
+ def run(segments, args, addresses, interpreter=None, **kwargs):
28
+ interpreter.checkpoints[args.name] = addresses
29
+ return addresses
@@ -0,0 +1,21 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from fa import utils
3
+
4
+ DESCRIPTION = '''clears the current result-set
5
+
6
+ EXAMPLE:
7
+ results = [0, 4, 8]
8
+ -> clear
9
+ results = []
10
+ '''
11
+
12
+
13
+ def get_parser():
14
+ p = utils.ArgumentParserNoExit('clear',
15
+ description=DESCRIPTION,
16
+ formatter_class=RawTextHelpFormatter)
17
+ return p
18
+
19
+
20
+ def run(segments, args, addresses, interpreter=None, **kwargs):
21
+ return []
@@ -0,0 +1,46 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from collections import OrderedDict
3
+ import binascii
4
+
5
+ from fa import utils
6
+
7
+ DESCRIPTION = '''expands the result-set with the occurrences of the given bytes
8
+
9
+ EXAMPLE:
10
+ 0x00000000: 01 02 03 04
11
+ 0x00000004: 05 06 07 08
12
+
13
+ results = []
14
+ -> find-bytes --or 01020304
15
+ result = [0]
16
+
17
+ -> find-bytes --or 05060708
18
+ results = [0, 4]
19
+ '''
20
+
21
+
22
+ def get_parser():
23
+ p = utils.ArgumentParserNoExit('find-bytes',
24
+ description=DESCRIPTION,
25
+ formatter_class=RawTextHelpFormatter)
26
+ p.add_argument('--or', action='store_true')
27
+ p.add_argument('hex_str')
28
+ return p
29
+
30
+
31
+ @utils.yield_unique
32
+ def find_bytes(hex_str, segments=None):
33
+ needle = binascii.unhexlify(''.join(hex_str.split(' ')))
34
+ return utils.find_raw(needle, segments=segments)
35
+
36
+
37
+ def run(segments, args, addresses, interpreter=None, **kwargs):
38
+ results = list(find_bytes(args.hex_str, segments=segments))
39
+
40
+ retval = set(addresses)
41
+ if getattr(args, 'or'):
42
+ retval.update(results)
43
+ else:
44
+ raise ValueError("must specify --or option")
45
+
46
+ return list(OrderedDict.fromkeys(retval))
@@ -0,0 +1,47 @@
1
+ from argparse import RawTextHelpFormatter
2
+ from collections import OrderedDict
3
+
4
+ from fa import utils, context
5
+
6
+ DESCRIPTION = '''expands the result-set with the occurrences of the given bytes
7
+ expression in "ida bytes syntax"
8
+
9
+ EXAMPLE:
10
+ 0x00000000: 01 02 03 04
11
+ 0x00000004: 05 06 07 08
12
+
13
+ results = []
14
+ -> find-bytes-ida --or '01 02 03 04'
15
+ result = [0]
16
+
17
+ -> find-bytes-ida --or '05 06 ?? 08'
18
+ results = [0, 4]
19
+ '''
20
+
21
+
22
+ def get_parser():
23
+ p = utils.ArgumentParserNoExit('find-bytes-ida',
24
+ description=DESCRIPTION,
25
+ formatter_class=RawTextHelpFormatter)
26
+ p.add_argument('--or', action='store_true')
27
+ p.add_argument('expression')
28
+ return p
29
+
30
+
31
+ @context.ida_context
32
+ @utils.yield_unique
33
+ def find_bytes_ida(expression, segments=None):
34
+ for address in utils.ida_find_all(expression):
35
+ yield address
36
+
37
+
38
+ def run(segments, args, addresses, interpreter=None, **kwargs):
39
+ results = find_bytes_ida(args.expression)
40
+
41
+ retval = set(addresses)
42
+ if getattr(args, 'or'):
43
+ retval.update(results)
44
+ else:
45
+ raise ValueError("must specify --or option")
46
+
47
+ return list(OrderedDict.fromkeys(retval))