-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathResolveARM32SyscallsScript.java
306 lines (273 loc) · 11.8 KB
/
ResolveARM32SyscallsScript.java
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
/* ###
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Uses overriding references and the symbolic propogator to resolve system calls
//@category Analysis
//@author Anthony DeRosa, Syscall 7, LLC
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Predicate;
import generic.jar.ResourceFile;
import ghidra.app.cmd.function.ApplyFunctionDataTypesCmd;
import ghidra.app.cmd.memory.AddUninitializedMemoryBlockCmd;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.plugin.core.analysis.ConstantPropagationContextEvaluator;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.opinion.ElfLoader;
import ghidra.framework.Application;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.BasicCompilerSpec;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ContextEvaluator;
import ghidra.program.util.SymbolicPropogator;
import ghidra.program.util.SymbolicPropogator.Value;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* This script will resolve system calls for ARMv7 (32-bit) binaries.
* It assumes that the syscall native instruction is used to make system calls,
* It should be straightforward to modify this script for other cases.
*/
public class ResolveARM32SyscallsScript extends GhidraScript {
private static final String ARM = "ARM";
private static final String SYSCALL_SPACE_NAME = "syscall";
private static final int SYSCALL_SPACE_LENGTH = 0x10000;
//this is the name of the userop (aka CALLOTHER) in the pcode translation of the
//native "syscall" instruction
private static final String SYSCALL_X64_CALLOTHER = "syscall";
//a set of names of all syscalls that do not return
private static final Set<String> noreturnSyscalls = Set.of("exit", "exit_group");
//tests whether an instruction is making a system call
private Predicate<Instruction> tester;
//datatype archive containing signature of system calls
private String datatypeArchiveName;
//file containing map from syscall numbers to syscall names
//note that different architectures can have different system call numbers, even
//if they're both Linux...
private String syscallFileName;
//the type of overriding reference to apply
private RefType overrideType;
//the calling convention to use for system calls (must be defined in the appropriate .cspec file)
private String callingConvention;
@Override
protected void run() throws Exception {
if (!currentProgram.getLanguage().getProcessor().toString().equals(ARM)) {
popup("This script is intended for x86 or x64 Linux files, not " + currentProgram.getLanguage().getProcessor().toString());
return;
}
int size = currentProgram.getLanguage().getLanguageDescription().getSize();
//determine whether the executable is 32 or 64 bit and set fields appropriately
if (size == 64) {
popup("This script currently only works for 32-bit ARM");
return;
}
else {
tester = ResolveARM32SyscallsScript::checkARM32Instruction;
overrideType = RefType.CALLOTHER_OVERRIDE_CALL;
datatypeArchiveName = "generic_clib";
callingConvention = "default";
// uncomment the following line to use the default Linux syscall numbers
//syscallFileName = "x86_linux_syscall_numbers";
}
//get the space where the system calls live.
//If it doesn't exist, create it.
AddressSpace syscallSpace =
currentProgram.getAddressFactory().getAddressSpace(SYSCALL_SPACE_NAME);
if (syscallSpace == null) {
//don't muck with address spaces if you don't have exclusive access to the program.
if (!currentProgram.hasExclusiveAccess()) {
popup("Must have exclusive access to " + currentProgram.getName() +
" to run this script");
return;
}
Address startAddr = currentProgram.getAddressFactory().getAddressSpace(
BasicCompilerSpec.OTHER_SPACE_NAME).getAddress(0x0L);
AddUninitializedMemoryBlockCmd cmd = new AddUninitializedMemoryBlockCmd(
SYSCALL_SPACE_NAME, null, this.getClass().getName(), startAddr,
SYSCALL_SPACE_LENGTH, true, true, true, false, true);
if (!cmd.applyTo(currentProgram)) {
popup("Failed to create " + SYSCALL_SPACE_NAME);
return;
}
syscallSpace = currentProgram.getAddressFactory().getAddressSpace(SYSCALL_SPACE_NAME);
}
else {
printf("AddressSpace %s found, continuing...\n", SYSCALL_SPACE_NAME);
}
//get all of the functions that contain system calls
//note that this will not find system call instructions that are not in defined functions
Map<Function, Set<Address>> funcsToCalls = getSyscallsInFunctions(currentProgram, monitor);
if (funcsToCalls.isEmpty()) {
popup("No system calls found (within defined functions)");
return;
}
//get the system call number at each callsite of a system call.
//note that this is not guaranteed to succeed at a given system call call site -
//it might be hard (or impossible) to determine a specific constant
Map<Address, Long> addressesToSyscalls =
resolveConstants(funcsToCalls, currentProgram, monitor);
if (addressesToSyscalls.isEmpty()) {
popup("Couldn't resolve any syscall constants");
return;
}
//get the map from system call numbers to system call names
//you might have to create this yourself!
Map<Long, String> syscallNumbersToNames = getSyscallNumberMap();
//at each system call call site where a constant could be determined, create
//the system call (if not already created), then add the appropriate overriding reference
//use syscallNumbersToNames to name the created functions
//if there's not a name corresponding to the constant use a default
for (Entry<Address, Long> entry : addressesToSyscalls.entrySet()) {
Address callSite = entry.getKey();
Long offset = entry.getValue();
Address callTarget = syscallSpace.getAddress(offset);
Function callee = currentProgram.getFunctionManager().getFunctionAt(callTarget);
if (callee == null) {
String funcName = String.format("syscall_0x%02X", offset);
if (syscallNumbersToNames.get(offset) != null) {
funcName = syscallNumbersToNames.get(offset);
}
callee = createFunction(callTarget, funcName);
callee.setCallingConvention(callingConvention);
//check if the function name is one of the non-returning syscalls
if (noreturnSyscalls.contains(funcName)) {
callee.setNoReturn(true);
}
}
Reference ref = currentProgram.getReferenceManager().addMemoryReference(callSite,
callTarget, overrideType, SourceType.USER_DEFINED, Reference.MNEMONIC);
//overriding references must be primary to be active
currentProgram.getReferenceManager().setPrimary(ref, true);
}
//finally, open the appropriate data type archive and apply its function data types
//to the new system call space, so that the system calls have the correct signatures
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(currentProgram);
DataTypeManagerService service = mgr.getDataTypeManagerService();
List<DataTypeManager> dataTypeManagers = new ArrayList<>();
dataTypeManagers.add(service.openDataTypeArchive(datatypeArchiveName));
dataTypeManagers.add(currentProgram.getDataTypeManager());
ApplyFunctionDataTypesCmd cmd = new ApplyFunctionDataTypesCmd(dataTypeManagers,
new AddressSet(syscallSpace.getMinAddress(), syscallSpace.getMaxAddress()),
SourceType.USER_DEFINED, false, false);
cmd.applyTo(currentProgram);
}
//TODO: better error checking!
private Map<Long, String> getSyscallNumberMap() {
Map<Long, String> syscallMap = new HashMap<>();
ResourceFile rFile = Application.findDataFileInAnyModule(syscallFileName);
if (rFile == null) {
popup("Error opening syscall number file, using default names");
return syscallMap;
}
try (FileReader fReader = new FileReader(rFile.getFile(false));
BufferedReader bReader = new BufferedReader(fReader)) {
String line = null;
while ((line = bReader.readLine()) != null) {
//lines starting with # are comments
if (!line.startsWith("#")) {
String[] parts = line.trim().split(" ");
Long number = Long.parseLong(parts[0]);
syscallMap.put(number, parts[1]);
}
}
}
catch (IOException e) {
Msg.showError(this, null, "Error reading syscall map file", e.getMessage(), e);
}
return syscallMap;
}
/**
* Scans through all of the functions defined in {@code program} and returns
* a map which takes a function to the set of address in its body which contain
* system calls
* @param program program containing functions
* @param tMonitor monitor
* @return map function -> addresses in function containing syscalls
* @throws CancelledException if the user cancels
*/
private Map<Function, Set<Address>> getSyscallsInFunctions(Program program,
TaskMonitor tMonitor) throws CancelledException {
Map<Function, Set<Address>> funcsToCalls = new HashMap<>();
for (Function func : program.getFunctionManager().getFunctionsNoStubs(true)) {
tMonitor.checkCanceled();
for (Instruction inst : program.getListing().getInstructions(func.getBody(), true)) {
if (tester.test(inst)) {
Set<Address> callSites = funcsToCalls.get(func);
if (callSites == null) {
callSites = new HashSet<>();
funcsToCalls.put(func, callSites);
}
callSites.add(inst.getAddress());
}
}
}
return funcsToCalls;
}
/**
* Uses the symbolic propogator to attempt to determine the constant value in
* the syscall register at each system call instruction
*
* @param funcsToCalls map from functions containing syscalls to address in each function of
* the system call
* @param program containing the functions
* @return map from addresses of system calls to system call numbers
* @throws CancelledException if the user cancels
*/
private Map<Address, Long> resolveConstants(Map<Function, Set<Address>> funcsToCalls,
Program program, TaskMonitor tMonitor) throws CancelledException {
Map<Address, Long> addressesToSyscalls = new HashMap<>();
for (Function func : funcsToCalls.keySet()) {
for (Address callSite : funcsToCalls.get(func)) {
long val = Long.decode(getInstructionAt(callSite).getDefaultOperandRepresentation(0));
addressesToSyscalls.put(callSite, val);
}
}
return addressesToSyscalls;
}
/**
* Checks whether an ARM native instruction is a system call
* @param inst instruction to check
* @return true precisely when the instruction is a system call
*/
private static boolean checkARM32Instruction(Instruction inst) {
String mnemonic = inst.getMnemonicString();
return mnemonic.equals("svc") || mnemonic.equals("swi");
}
/**
* Checks whether an x64 instruction is a system call
* @param inst instruction to check
* @return true precisely when the instruction is a system call
*/
private static boolean checkX64Instruction(Instruction inst) {
boolean retVal = false;
for (PcodeOp op : inst.getPcode()) {
if (op.getOpcode() == PcodeOp.CALLOTHER) {
int index = (int) op.getInput(0).getOffset();
if (inst.getProgram().getLanguage().getUserDefinedOpName(index).equals(
SYSCALL_X64_CALLOTHER)) {
retVal = true;
}
}
}
return retVal;
}
}