├── .github
├── FUNDING.yml
└── workflows
│ └── release.yml
├── .gitignore
├── README.md
└── findyara.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: oalabs
2 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 |
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: ncipollo/release-action@v1
15 | with:
16 | artifacts: "findyara.py"
17 | omitBody: true
18 | token: ${{ secrets.GITHUB_TOKEN }}
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Apple crap
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 | # Byte-compiled / optimized / DLL files
7 | __pycache__/
8 | *.py[cod]
9 | *$py.class
10 |
11 | # C extensions
12 | *.so
13 |
14 | # Distribution / packaging
15 | .Python
16 | build/
17 | develop-eggs/
18 | dist/
19 | downloads/
20 | eggs/
21 | .eggs/
22 | lib/
23 | lib64/
24 | parts/
25 | sdist/
26 | var/
27 | wheels/
28 | pip-wheel-metadata/
29 | share/python-wheels/
30 | *.egg-info/
31 | .installed.cfg
32 | *.egg
33 | MANIFEST
34 |
35 | # PyInstaller
36 | # Usually these files are written by a python script from a template
37 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
38 | *.manifest
39 | *.spec
40 |
41 | # Installer logs
42 | pip-log.txt
43 | pip-delete-this-directory.txt
44 |
45 | # Unit test / coverage reports
46 | htmlcov/
47 | .tox/
48 | .nox/
49 | .coverage
50 | .coverage.*
51 | .cache
52 | nosetests.xml
53 | coverage.xml
54 | *.cover
55 | *.py,cover
56 | .hypothesis/
57 | .pytest_cache/
58 |
59 | # Translations
60 | *.mo
61 | *.pot
62 |
63 | # Django stuff:
64 | *.log
65 | local_settings.py
66 | db.sqlite3
67 | db.sqlite3-journal
68 |
69 | # Flask stuff:
70 | instance/
71 | .webassets-cache
72 |
73 | # Scrapy stuff:
74 | .scrapy
75 |
76 | # Sphinx documentation
77 | docs/_build/
78 |
79 | # PyBuilder
80 | target/
81 |
82 | # Jupyter Notebook
83 | .ipynb_checkpoints
84 |
85 | # IPython
86 | profile_default/
87 | ipython_config.py
88 |
89 | # pyenv
90 | .python-version
91 |
92 | # pipenv
93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
96 | # install all needed dependencies.
97 | #Pipfile.lock
98 |
99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
100 | __pypackages__/
101 |
102 | # Celery stuff
103 | celerybeat-schedule
104 | celerybeat.pid
105 |
106 | # SageMath parsed files
107 | *.sage.py
108 |
109 | # Environments
110 | .env
111 | .venv
112 | env/
113 | venv/
114 | ENV/
115 | env.bak/
116 | venv.bak/
117 |
118 | # Spyder project settings
119 | .spyderproject
120 | .spyproject
121 |
122 | # Rope project settings
123 | .ropeproject
124 |
125 | # mkdocs documentation
126 | /site
127 |
128 | # mypy
129 | .mypy_cache/
130 | .dmypy.json
131 | dmypy.json
132 |
133 | # Pyre type checker
134 | .pyre/
135 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://github.com/OALabs/findyara-ida/releases) [](https://discord.gg/cw4U3WHvpn) [](https://www.patreon.com/oalabs)
6 |
7 | # FindYara
8 | Use this IDA python plugin to scan your binary with yara rules. All the yara rule matches will be listed with their offset so you can quickly jump to them!
9 |
10 | **:beers: All credit for this plugin and the code goes to David Berard (@_p0ly_) :beers:**
11 |
12 | This plugin is copied from David's excellent [findcrypt-yara plugin](https://github.com/polymorf/findcrypt-yara). This plugin just extends his to use any yara rule.
13 |
14 | ## Using FindYara
15 | The plugin can be launched from the menu using `Edit->Plugins->FindYara` or using the hot-key combination `Ctrl-Alt-Y`. When launched the FindYara will open a file selection dialogue that allows you to select your Yara rules file. Once the rule file has been selected FindYara will scan the loaded binary for rule matches.
16 |
17 | All rule matches are displayed in a selection box that allows you to double click the matches and jump to their location in the binary.
18 |
19 | ### Rules Not Matching Binary
20 | FindYara scans the loaded PE sections in IDA, this means that yara rules that include matches on the PE header **will not match in IDA**. IDA does not load the PE header as a scannable section. Also, if you have not selected `Load resources` when loading your binary in IDA then the resources section will be unavailable for scanning.
21 |
22 | This can lead to frustrating situations where a yara rule will match outside of IDA but not when using FindYara. If you encounter this try editing the yara rule to remove the matches on the PE header and resources sections.
23 |
24 | ## Installing FindYara
25 | Before using the plugin you must install the python Yara module in your IDA environment. The simplest way to do this is to use pip from a shell outside of IDA.
26 | `pip install yara-python`.
27 |
28 | **Do not install the `yara` module by mistake.** The `yara` python module will mess with your `yara-python` module so it must be uninstalled if it was installed by mistake.
29 |
30 | Once you have the yara module installed simply copy the latest release of [`findyara.py`](https://github.com/OALabs/findyara-ida/releases) into your IDA plugins directory and you are ready to start Yara scanning!
31 |
32 | ## ❗Compatibility Issues
33 | FindYara has been developed for use with the __IDA 7+__ and __Python 3__ it is not backwards compatible.
34 |
35 | FindYara requires a the python **Yara** module with version **4+** installed. Earlier versions of Yara are not compatible with the plugin and may cause issues due to breaking changes in the Yara match format.
36 |
37 | ## Acknowledgments
38 | A huge thank you to David Berard (@_p0ly_) - [Follow him on GitHub here](https://github.com/polymorf/)! This is mostly his code and he gets all the credit for the original plugin framework.
39 |
40 | Also, hat tip to Alex Hanel @nullandnull - [Follow him on GitHub here](https://github.com/alexander-hanel). Alex helped me sort through how the IDC methods are being used. His [IDA Python book](https://leanpub.com/IDAPython-Book) is a fantastic reference!!
41 |
42 |
--------------------------------------------------------------------------------
/findyara.py:
--------------------------------------------------------------------------------
1 | ########################################################################################
2 | ##
3 | ## All credit to David Berard (@_p0ly_) https://github.com/polymorf/findcrypt-yara
4 | ##
5 | ## This plugin is simply a copy of his excellent findcrypt-yara plugin only expanded
6 | ## use allow searching for any yara rules.
7 | ##
8 | ## ____ __ __ __ ____ _ _ ___ ____ ___
9 | ## || || ||\ || || \\ \\// // \\ || \\ // \\
10 | ## ||== || ||\\|| || )) )/ ||=|| ||_// ||=||
11 | ## || || || \|| ||_// // || || || \\ || ||
12 | ##
13 | ## IDA plugin for Yara scanning... find those Yara matches!
14 | ##
15 | ## Updated for IDA 7.xx and Python 3
16 | ##
17 | ## To install:
18 | ## Copy script into plugins directory, i.e: C:\Program Files\\plugins
19 | ##
20 | ## To run:
21 | ## Ctl+Alt+Y or Edit->Plugins->FindYara
22 | ## Use the dialogue box to select your yara rule file and start scanning!
23 | ##
24 | ########################################################################################
25 |
26 | import idaapi
27 | import idautils
28 | import ida_bytes
29 | import idc
30 | import ida_kernwin
31 | import yara
32 | import string
33 |
34 | __AUTHOR__ = '@herrcore'
35 |
36 | PLUGIN_NAME = "FindYara"
37 | PLUGIN_HOTKEY = "Ctrl-Alt-Y"
38 | VERSION = '3.3.0'
39 |
40 | try:
41 | class Kp_Menu_Context(idaapi.action_handler_t):
42 | def __init__(self):
43 | idaapi.action_handler_t.__init__(self)
44 |
45 |
46 | @classmethod
47 | def get_name(self):
48 | return self.__name__
49 |
50 |
51 | @classmethod
52 | def get_label(self):
53 | return self.label
54 |
55 |
56 | @classmethod
57 | def register(self, plugin, label):
58 | self.plugin = plugin
59 | self.label = label
60 | instance = self()
61 | return idaapi.register_action(idaapi.action_desc_t(
62 | self.get_name(), # Name. Acts as an ID. Must be unique.
63 | instance.get_label(), # Label. That's what users see.
64 | instance # Handler. Called when activated, and for updating
65 | ))
66 |
67 |
68 | @classmethod
69 | def unregister(self):
70 | """Unregister the action.
71 | After unregistering the class cannot be used.
72 | """
73 | idaapi.unregister_action(self.get_name())
74 |
75 |
76 | @classmethod
77 | def activate(self, ctx):
78 | # dummy method
79 | return 1
80 |
81 |
82 | @classmethod
83 | def update(self, ctx):
84 | if ctx.form_type == idaapi.BWN_DISASM:
85 | return idaapi.AST_ENABLE_FOR_WIDGET
86 | return idaapi.AST_DISABLE_FOR_WIDGET
87 |
88 |
89 | class Searcher(Kp_Menu_Context):
90 | def activate(self, ctx):
91 | self.plugin.search()
92 | return 1
93 |
94 | except:
95 | pass
96 |
97 | p_initialized = False
98 |
99 |
100 |
101 | class YaraSearchResultChooser(idaapi.Choose):
102 | def __init__(self, title, items, flags=0, width=None, height=None, embedded=False, modal=False):
103 | idaapi.Choose.__init__(
104 | self,
105 | title,
106 | [
107 | ["Address", idaapi.Choose.CHCOL_HEX|10],
108 | ["Rule Name", idaapi.Choose.CHCOL_PLAIN|20],
109 | ["Match Name", idaapi.Choose.CHCOL_PLAIN|20],
110 | ["Match", idaapi.Choose.CHCOL_PLAIN|40],
111 | ["Type", idaapi.Choose.CHCOL_PLAIN|10],
112 | ],
113 | flags=flags,
114 | width=width,
115 | height=height,
116 | embedded=embedded)
117 | self.items = items
118 | self.selcount = 0
119 | self.n = len(items)
120 |
121 |
122 | def OnClose(self):
123 | return
124 |
125 |
126 | def OnSelectLine(self, n):
127 | self.selcount += 1
128 | ida_kernwin.jumpto(self.items[n][0])
129 |
130 |
131 | def OnGetLine(self, n):
132 | res = self.items[n]
133 | res = [idc.atoa(res[0]), res[1], res[2], res[3], res[4]]
134 | return res
135 |
136 |
137 | def OnGetSize(self):
138 | n = len(self.items)
139 | return n
140 |
141 |
142 | def show(self):
143 | return self.Show() >= 0
144 |
145 | #--------------------------------------------------------------------------
146 | # Plugin
147 | #--------------------------------------------------------------------------
148 | class FindYara_Plugin_t(idaapi.plugin_t):
149 | comment = "FindYara plugin for IDA Pro (using yara framework)"
150 | help = ""
151 | wanted_name = PLUGIN_NAME
152 | wanted_hotkey = PLUGIN_HOTKEY
153 | flags = idaapi.PLUGIN_KEEP
154 |
155 |
156 | def init(self):
157 | global p_initialized
158 |
159 | # register popup menu handlers
160 | try:
161 | Searcher.register(self, "FindYara")
162 | except:
163 | pass
164 |
165 | if p_initialized is False:
166 | p_initialized = True
167 | idaapi.register_action(idaapi.action_desc_t(
168 | "FindYara",
169 | "Find Yara rule matches!",
170 | self.search,
171 | None,
172 | None,
173 | 0))
174 | idaapi.attach_action_to_menu("Edit/FindYara", "FindYara", idaapi.SETMENU_APP)
175 | ## Print a nice header
176 | print("=" * 80)
177 | print(" ____ __ __ __ ____ _ _ ___ ____ ___ ")
178 | print(r" || || ||\ || || \\ \\// // \\ || \\ // \\")
179 | print(r" ||== || ||\\|| || )) )/ ||=|| ||_// ||=||")
180 | print(r" || || || \|| ||_// // || || || \\ || ||")
181 | print("\nFindYara v{0} by @herrcore".format(VERSION))
182 | print("* All credit to David Berard (@_p0ly_) for the code! *")
183 | print("* This is a slightly modified version of findcrypt-yara *")
184 | print("\nFindYara search shortcut key is Ctrl-Alt-y")
185 | print("=" * 80)
186 |
187 | return idaapi.PLUGIN_KEEP
188 |
189 |
190 | def term(self):
191 | pass
192 |
193 |
194 | def toVirtualAddress(self, offset, segments):
195 | va_offset = 0
196 | for seg in segments:
197 | if seg[1] <= offset < seg[2]:
198 | va_offset = seg[0] + (offset - seg[1])
199 | return va_offset
200 |
201 |
202 | def search(self, yara_file):
203 | memory, offsets = self._get_memory()
204 | try:
205 | rules = yara.compile(yara_file)
206 | except:
207 | print("ERROR: Cannot compile Yara rules from %s" % yara_file)
208 | return
209 | values = self.yarasearch(memory, offsets, rules)
210 | c = YaraSearchResultChooser("FindYara scan results", values)
211 | r = c.show()
212 |
213 |
214 | def yarasearch(self, memory, offsets, rules):
215 | values = list()
216 | matches = rules.match(data=memory)
217 | for rule_match in matches:
218 | name = rule_match.rule
219 | for match in rule_match.strings:
220 | match_string = match[2]
221 | match_type = 'unknown'
222 | if all(chr(c) in string.printable for c in match_string):
223 | match_string = match_string.decode('utf-8')
224 | match_type = 'ascii string'
225 | elif all(chr(c) in string.printable+'\x00' for c in match_string) and (b'\x00\x00' not in match_string):
226 | match_string = match_string.decode('utf-16')
227 | match_type = 'wide string'
228 | else:
229 | match_string = " ".join("{:02x}".format(c) for c in match_string)
230 | match_type = 'binary'
231 |
232 | value = [
233 | self.toVirtualAddress(match[0], offsets),
234 | name,
235 | match[1],
236 | match_string,
237 | match_type
238 | ]
239 | values.append(value)
240 | return values
241 |
242 |
243 | def _get_memory(self):
244 | result = bytearray()
245 | segment_starts = [ea for ea in idautils.Segments()]
246 | offsets = []
247 | start_len = 0
248 | for start in segment_starts:
249 | end = idc.get_segm_attr(start, idc.SEGATTR_END)
250 | result += ida_bytes.get_bytes(start, end - start)
251 | offsets.append((start, start_len, len(result)))
252 | start_len = len(result)
253 | return bytes(result), offsets
254 |
255 |
256 | def run(self, arg):
257 | yara_file = ida_kernwin.ask_file(0, "*.yara", 'Choose Yara File...')
258 | if yara_file == None:
259 | print("ERROR: You must choose a yara file to scan with")
260 | else:
261 | self.search(yara_file)
262 |
263 |
264 | # register IDA plugin
265 | def PLUGIN_ENTRY():
266 | return FindYara_Plugin_t()
267 |
--------------------------------------------------------------------------------