-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathbatch_search_c_formatter.py
More file actions
83 lines (71 loc) · 2.97 KB
/
Copy pathbatch_search_c_formatter.py
File metadata and controls
83 lines (71 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""Example: register a C-style batch search formatter from IDA Python.
For a persistent install, copy the formatter registration code you want into
``idapythonrc.py`` in your IDA user directory (``$IDAUSR/idapythonrc.py``).
IDA sources that file during startup, so the formatter will be registered for
each new IDA session.
If SigMaker is not already importable from IDA Python, add the SigMaker plugin
directory to ``sys.path`` before the formatter code. You can also paste this
file into IDA's Python console after loading the plugin for one-off testing.
Executing this module registers `.c`, `.h`, `.hpp`, and `.cpp` export handling
without making C output a built-in sigmaker format. It is an example template,
not a file users need to import directly. Registration rejects an existing
formatter name or suffix; add ``override=True`` to the decorator only when the
replacement is intentional.
"""
import re
import sigmaker
@sigmaker.BatchSearchFormatter.register(
"c",
suffixes=(".c", ".h", ".hpp", ".cpp"),
)
class CBatchSearchFormatter:
@staticmethod
def _symbol_name(name: str, fallback: str) -> str:
cleaned = re.sub(r"\W+", "_", name).strip("_")
if not cleaned:
cleaned = fallback
if cleaned[0].isdigit():
cleaned = "_" + cleaned
return cleaned
@staticmethod
def _comment(text: str) -> str:
return text.replace("*/", "* /").replace("\n", " ")
def format(self, results: sigmaker.BatchSearchResults) -> str:
lines = [
"/* SigMaker batch search address results. */",
"#include <stdint.h>",
"",
]
if results.imagebase is not None:
lines.extend(
[
f"static const uint64_t sigmaker_imagebase = "
f"0x{results.imagebase:X}ULL;",
"",
]
)
for idx, entry in enumerate(results, start=1):
name = self._symbol_name(
entry.name or entry.display_name,
f"pattern_{idx}",
)
if entry.error:
lines.append(f"/* {name}: error - {self._comment(entry.error)} */")
continue
if not entry.matches:
lines.append(f"/* {name}: no matches */")
continue
if len(entry.matches) != 1:
lines.append(f"/* {name}: {len(entry.matches)} matches */")
continue
hit = entry.matches[0]
lines.append(f"static const uint64_t {name}_ea = {hit:ea}ULL;")
if hit.rva is not None:
lines.append(f"static const uint64_t {name}_rva = {hit:rva}ULL;")
file_offset = entry.file_offset_for_match(hit)
if file_offset is not None:
lines.append(
f"static const uint64_t {name}_file_offset = "
f"0x{file_offset:X}ULL;"
)
return "\n".join(lines).rstrip() + "\n"