-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy path1543-Add-x86-real-mode-interrupt-analyzer-plugin.patch
332 lines (329 loc) · 11.9 KB
/
1543-Add-x86-real-mode-interrupt-analyzer-plugin.patch
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alessandro Gatti <[email protected]>
Date: Mon, 17 Feb 2020 19:12:41 +0100
Subject: [PATCH] 1543: Add x86 real mode interrupt analyzer plugin.
Right now this only recognises the two interrupts that can trigger a
program end (20h and 21h/4Ch) and end the function they are found in,
but this can be extended to cover the full range of interrupts available
in a common x86 real mode system.
---
Ghidra/Processors/x86/certification.manifest | 2 +
.../core/analysis/DOSInterruptAnalyzer.java | 265 ++++++++++++++++++
.../core/analysis/InterruptAnalyzer.java | 25 ++
3 files changed, 292 insertions(+)
create mode 100644 Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/DOSInterruptAnalyzer.java
create mode 100644 Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/InterruptAnalyzer.java
diff --git a/Ghidra/Processors/x86/certification.manifest b/Ghidra/Processors/x86/certification.manifest
index 8d64f3e60b..b90a299d24 100644
--- a/Ghidra/Processors/x86/certification.manifest
+++ b/Ghidra/Processors/x86/certification.manifest
@@ -94,3 +94,5 @@ data/patterns/x86delphi_patterns.xml||GHIDRA||||END|
data/patterns/x86gcc_patterns.xml||GHIDRA||||END|
data/patterns/x86win_patterns.xml||GHIDRA||||END|
data/patterns/x86win_prepatterns.xml||GHIDRA||||END|
+src/main/java/ghidra/app/plugin/core/analysis/DOSInterruptAnalyzer.java||GHIDRA||||END|
+src/main/java/ghidra/app/plugin/core/analysis/InterruptAnalyzer.java||GHIDRA||||END|
diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/DOSInterruptAnalyzer.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/DOSInterruptAnalyzer.java
new file mode 100644
index 0000000000..5896a996e9
--- /dev/null
+++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/DOSInterruptAnalyzer.java
@@ -0,0 +1,265 @@
+package ghidra.app.plugin.core.analysis;
+
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+
+import ghidra.app.services.AbstractAnalyzer;
+import ghidra.app.services.AnalysisPriority;
+import ghidra.app.services.AnalyzerType;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.program.database.function.OverlappingFunctionException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOutOfBoundsException;
+import ghidra.program.model.address.AddressSet;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.lang.Register;
+import ghidra.program.model.lang.RegisterValue;
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Instruction;
+import ghidra.program.model.listing.InstructionIterator;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.scalar.Scalar;
+import ghidra.program.util.ContextEvaluatorAdapter;
+import ghidra.program.util.SymbolicPropogator;
+import ghidra.program.util.VarnodeContext;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+public class DOSInterruptAnalyzer extends AbstractAnalyzer {
+
+ private static final String NAME = "Resolve DOS interrupts";
+ private static final String DESCRIPTION = "Resolves selected DOS interrupt calls.";
+ private static final String LANGUAGE = "x86:LE:16:Real Mode";
+
+ /** Registered interrupt analyzers container. */
+ private Map<Integer, InterruptAnalyzer> interruptAnalyzers = new HashMap<>();
+
+ public DOSInterruptAnalyzer() {
+ this(NAME, DESCRIPTION, AnalyzerType.FUNCTION_ANALYZER);
+ }
+
+ public DOSInterruptAnalyzer(String name, String description, AnalyzerType type) {
+ super(name, description, type);
+ setDefaultEnablement(true);
+ setPriority(AnalysisPriority.FUNCTION_ANALYSIS.after());
+
+ addInterruptAnalyzer(0x20, new Int20hAnalyzer());
+ addInterruptAnalyzer(0x21, new Int21hAnalyzer());
+ }
+
+ /**
+ * Attaches the given interrupt analyzer to a specific interrupt number.
+ *
+ * At the moment there is no provision for interrupt analyzers chaining.
+ *
+ * @param number the number of the interrupt to attach to.
+ * @param analyzer the analyzer to attach.
+ * @return true if the analyzer was successfully attached, false otherwise.
+ */
+ public boolean addInterruptAnalyzer(int number, InterruptAnalyzer analyzer) {
+ if (number < 0 || number > 255 || analyzer == null || interruptAnalyzers.containsKey(number)) {
+ return false;
+ }
+
+ interruptAnalyzers.put(number, analyzer);
+ return true;
+ }
+
+ /**
+ * Detaches analyzers from a specific interrupt number.
+ *
+ * At the moment there is no provision for interrupt analyzer chaining.
+ *
+ * @param number the number of the interrupt to detach analyzers from.
+ * @return true if the analyzer was successfully detached, false otherwise.
+ */
+ public void removeInterruptAnalyzer(int number) {
+ if (number < 0 || number > 255 || !interruptAnalyzers.containsKey(number)) {
+ return;
+ }
+
+ interruptAnalyzers.remove(number);
+ }
+
+ @Override
+ public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
+ throws CancelledException {
+ for (Function function : program.getFunctionManager().getFunctions(true)) {
+ AddressSetView addressSetView = function.getBody();
+ InstructionIterator instructionIterator = program.getListing().getInstructions(addressSetView, true);
+
+ boolean shouldContinue = true;
+ while (instructionIterator.hasNext() && shouldContinue) {
+ shouldContinue = analyzeInstruction(function, instructionIterator.next(), monitor);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean canAnalyze(Program program) {
+ return program.getLanguage().getLanguageID().getIdAsString().equals(LANGUAGE);
+ }
+
+ /**
+ * Analyzes a single instruction and if it is an interrupt call invokes a
+ * secondary analyzer for more in-depth processing.
+ *
+ * @param function the function the given instruction belongs to.
+ * @param instruction the instruction to analyze.
+ * @param monitor the task monitor to detect cancellation requests.
+ * @return true whether analysis should continue to the following
+ * instruction, false otherwise.
+ * @throws CancelledException if analysis has been cancelled by the user
+ * in the meantime.
+ */
+ private boolean analyzeInstruction(Function function, Instruction instruction, TaskMonitor monitor)
+ throws CancelledException {
+
+ if (!instruction.getMnemonicString().equals("INT") || instruction.getNumOperands() <= 0) {
+ return true;
+ }
+
+ int interruptNumber = (int) ((Scalar) instruction.getScalar(0)).getValue();
+ InterruptAnalyzer analyzer = interruptAnalyzers.getOrDefault(interruptNumber, null);
+ if (analyzer != null) {
+ return analyzer.processInstruction(function, instruction, monitor);
+ }
+
+ return true;
+ }
+
+ /**
+ * Utility class to track assignments to a particular register from a propagator.
+ */
+ static class AssignmentContextEvaluator extends ContextEvaluatorAdapter {
+
+ /** The register to track assignments to. */
+ private Register targetRegister;
+
+ /** The last address to track assignments in. */
+ private Address endAddress;
+
+ /** The last seen register value. */
+ private RegisterValue registerValue;
+
+ /**
+ * Creates the evaluator instance.
+ *
+ * @param target the register to track assignments to.
+ * @param end the last address to track assignments in.
+ */
+ public AssignmentContextEvaluator(Register target, Address end) {
+ targetRegister = target;
+ endAddress = end;
+ }
+
+ @Override
+ public boolean evaluateContext(VarnodeContext context, Instruction instr) {
+ RegisterValue value = context.getRegisterValue(targetRegister);
+ if (value != null) {
+ registerValue = value;
+ }
+
+ return instr.getAddress().equals(endAddress);
+ }
+
+ /**
+ * Returns the last seen register value.
+ *
+ * @return the last seen register value.
+ */
+ public RegisterValue getRegisterValue() {
+ return registerValue;
+ }
+ }
+
+ /**
+ * DOS INT 20h analyzer.
+ *
+ * All calls to INT 20h, regardless of the registers state should imply
+ * the immediate program termination.
+ *
+ * If an INT 20h instruction is found, the current function is resized
+ * to have its end address as the address where the opcode was seen.
+ */
+ static private class Int20hAnalyzer implements InterruptAnalyzer {
+
+ @Override
+ public boolean processInstruction(Function function, Instruction instruction, TaskMonitor monitor)
+ throws CancelledException {
+
+ // Resize the current function.
+
+ Address instructionAddress = instruction.getAddress();
+ if (!function.getBody().getMaxAddress().equals(instructionAddress)) {
+ try {
+ function.setBody(new AddressSet(function.getEntryPoint(), instructionAddress));
+ } catch (AddressOutOfBoundsException | OverlappingFunctionException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // No further function analysis is needed.
+
+ return false;
+ }
+ }
+
+ /**
+ * DOS INT 21h analyzer.
+ *
+ * INT 21h is the entry point for most if not all DOS functions, therefore
+ * analysis is a bit more complex. At this time only function 0x4C is
+ * handled, which signals the program termination.
+ *
+ * If an INT 21h instruction with AH being 0x4C is found, the current
+ * function is resized to have its end address as the address where the
+ * opcode was seen.
+ */
+ static private class Int21hAnalyzer implements InterruptAnalyzer {
+
+ @Override
+ public boolean processInstruction(Function function, Instruction instruction, TaskMonitor monitor)
+ throws CancelledException {
+
+ Address instructionAddress = instruction.getAddress();
+
+ if (function.getBody().getMaxAddress().equals(instructionAddress)) {
+
+ // End of function reached.
+
+ return false;
+ }
+
+ // Get the value of AH at this point in the function.
+
+ AssignmentContextEvaluator contextEvaluator = new AssignmentContextEvaluator(
+ instruction.getRegister("AH"), function.getBody().getMaxAddress());
+ SymbolicPropogator propagator = new SymbolicPropogator(function.getProgram());
+ propagator.flowConstants(function.getEntryPoint(), null, contextEvaluator, true, monitor);
+
+ // If AH was detected being 0x4C, resize the current function.
+
+ RegisterValue callCode = contextEvaluator.getRegisterValue();
+ if (callCode != null) {
+ BigInteger value = callCode.getUnsignedValue();
+ if (value != null && value.intValue() == 0x4C) {
+ try {
+ function.setBody(new AddressSet(function.getEntryPoint(), instructionAddress));
+ } catch (AddressOutOfBoundsException | OverlappingFunctionException e) {
+ e.printStackTrace();
+ }
+
+ // No further function analysis is needed.
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/InterruptAnalyzer.java b/Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/InterruptAnalyzer.java
new file mode 100644
index 0000000000..f29a1df4e9
--- /dev/null
+++ b/Ghidra/Processors/x86/src/main/java/ghidra/app/plugin/core/analysis/InterruptAnalyzer.java
@@ -0,0 +1,25 @@
+package ghidra.app.plugin.core.analysis;
+
+import ghidra.program.model.listing.Function;
+import ghidra.program.model.listing.Instruction;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * x86 Interrupt analyzer interface.
+ */
+public interface InterruptAnalyzer {
+
+ /**
+ * Interrupt handler callback.
+ *
+ * @param function the function the given instruction belongs to.
+ * @param instruction the instruction to analyze.
+ * @param monitor the task monitor to detect cancellation requests.
+ * @return true whether analysis should continue to the following
+ * instruction, false otherwise.
+ * @throws CancelledException if analysis has been cancelled by the user
+ * in the meantime.
+ */
+ boolean processInstruction(Function function, Instruction instruction, TaskMonitor monitor) throws CancelledException;
+}
--
2.45.1