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.
- fa-0.1.0/PKG-INFO +11 -0
- fa-0.1.0/README.md +418 -0
- fa-0.1.0/fa/__init__.py +0 -0
- fa-0.1.0/fa/commands/__init__.py +0 -0
- fa-0.1.0/fa/commands/add_offset_range.py +33 -0
- fa-0.1.0/fa/commands/alias +8 -0
- fa-0.1.0/fa/commands/align.py +26 -0
- fa-0.1.0/fa/commands/back.py +29 -0
- fa-0.1.0/fa/commands/back_to_checkpoint.py +28 -0
- fa-0.1.0/fa/commands/checkpoint.py +29 -0
- fa-0.1.0/fa/commands/clear.py +21 -0
- fa-0.1.0/fa/commands/find_bytes.py +46 -0
- fa-0.1.0/fa/commands/find_bytes_ida.py +47 -0
- fa-0.1.0/fa/commands/find_immediate.py +54 -0
- fa-0.1.0/fa/commands/find_str.py +42 -0
- fa-0.1.0/fa/commands/function_end.py +42 -0
- fa-0.1.0/fa/commands/function_lines.py +48 -0
- fa-0.1.0/fa/commands/function_start.py +48 -0
- fa-0.1.0/fa/commands/goto_ref.py +57 -0
- fa-0.1.0/fa/commands/keystone_find_opcodes.py +55 -0
- fa-0.1.0/fa/commands/keystone_verify_opcodes.py +57 -0
- fa-0.1.0/fa/commands/locate.py +38 -0
- fa-0.1.0/fa/commands/make_code.py +23 -0
- fa-0.1.0/fa/commands/make_comment.py +43 -0
- fa-0.1.0/fa/commands/make_function.py +23 -0
- fa-0.1.0/fa/commands/make_literal.py +23 -0
- fa-0.1.0/fa/commands/make_unknown.py +23 -0
- fa-0.1.0/fa/commands/max_xrefs.py +32 -0
- fa-0.1.0/fa/commands/min_xrefs.py +32 -0
- fa-0.1.0/fa/commands/most_common.py +28 -0
- fa-0.1.0/fa/commands/offset.py +27 -0
- fa-0.1.0/fa/commands/print.py +17 -0
- fa-0.1.0/fa/commands/run.py +15 -0
- fa-0.1.0/fa/commands/set_name.py +25 -0
- fa-0.1.0/fa/commands/set_type.py +37 -0
- fa-0.1.0/fa/commands/single.py +29 -0
- fa-0.1.0/fa/commands/sort.py +26 -0
- fa-0.1.0/fa/commands/trace.py +18 -0
- fa-0.1.0/fa/commands/unique.py +30 -0
- fa-0.1.0/fa/commands/verify_aligned.py +26 -0
- fa-0.1.0/fa/commands/verify_bytes.py +52 -0
- fa-0.1.0/fa/commands/verify_name.py +23 -0
- fa-0.1.0/fa/commands/verify_operand.py +77 -0
- fa-0.1.0/fa/commands/verify_ref.py +55 -0
- fa-0.1.0/fa/commands/verify_segment.py +43 -0
- fa-0.1.0/fa/commands/verify_str.py +33 -0
- fa-0.1.0/fa/commands/xref.py +25 -0
- fa-0.1.0/fa/commands/xrefs_to.py +53 -0
- fa-0.1.0/fa/context.py +51 -0
- fa-0.1.0/fa/fa_types.py +93 -0
- fa-0.1.0/fa/fainterp.py +483 -0
- fa-0.1.0/fa/ida_plugin.py +684 -0
- fa-0.1.0/fa/res/icons/create_sig.png +0 -0
- fa-0.1.0/fa/res/icons/export.png +0 -0
- fa-0.1.0/fa/res/icons/find.png +0 -0
- fa-0.1.0/fa/res/icons/find_all.png +0 -0
- fa-0.1.0/fa/res/icons/save.png +0 -0
- fa-0.1.0/fa/res/icons/settings.png +0 -0
- fa-0.1.0/fa/res/icons/suitcase.png +0 -0
- fa-0.1.0/fa/utils.py +94 -0
- fa-0.1.0/fa.egg-info/PKG-INFO +11 -0
- fa-0.1.0/fa.egg-info/SOURCES.txt +65 -0
- fa-0.1.0/fa.egg-info/dependency_links.txt +1 -0
- fa-0.1.0/fa.egg-info/requires.txt +6 -0
- fa-0.1.0/fa.egg-info/top_level.txt +1 -0
- fa-0.1.0/setup.cfg +4 -0
- fa-0.1.0/setup.py +28 -0
fa-0.1.0/PKG-INFO
ADDED
fa-0.1.0/README.md
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+

|
|
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
|
+

|
|
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
|
+
|
fa-0.1.0/fa/__init__.py
ADDED
|
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))
|