Advertisement
Guest User

Untitled

a guest
Oct 24th, 2024
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.94 KB | None | 0 0
  1. /* ###
  2. * IP: GHIDRA
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. //Uses overriding references and the symbolic propogator to resolve system calls
  18. //@category Analysis
  19.  
  20. import java.io.*;
  21. import java.util.*;
  22. import java.util.Map.Entry;
  23. import java.util.function.Predicate;
  24.  
  25. import generic.jar.ResourceFile;
  26. import ghidra.app.cmd.function.ApplyFunctionDataTypesCmd;
  27. import ghidra.app.cmd.memory.AddUninitializedMemoryBlockCmd;
  28. import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
  29. import ghidra.app.plugin.core.analysis.ConstantPropagationContextEvaluator;
  30. import ghidra.app.script.GhidraScript;
  31. import ghidra.app.services.DataTypeManagerService;
  32. import ghidra.app.util.opinion.ElfLoader;
  33. import ghidra.framework.Application;
  34. import ghidra.program.model.address.*;
  35. import ghidra.program.model.data.DataTypeManager;
  36. import ghidra.program.model.lang.Register;
  37. import ghidra.program.model.lang.SpaceNames;
  38. import ghidra.program.model.listing.*;
  39. import ghidra.program.model.mem.MemoryAccessException;
  40. import ghidra.program.model.pcode.PcodeOp;
  41. import ghidra.program.model.symbol.*;
  42. import ghidra.program.util.ContextEvaluator;
  43. import ghidra.program.util.SymbolicPropogator;
  44. import ghidra.program.util.SymbolicPropogator.Value;
  45. import ghidra.util.Msg;
  46. import ghidra.util.exception.CancelledException;
  47. import ghidra.util.task.TaskMonitor;
  48.  
  49. /**
  50. * This script will resolve system calls for x86 or x64 Linux binaries.
  51. * It assumes that in the x64 case, the syscall native instruction is used to make system calls,
  52. * and in the x86 case, system calls are made via an indirect call to GS:[0x10].
  53. * It should be straightforward to modify this script for other cases.
  54. */
  55. public class ResolveX86orX64LinuxSyscallsScript2 extends GhidraScript {
  56.  
  57. //disassembles to "CALL dword ptr GS:[0x10]"
  58. private static final byte[] x86_bytes = { 0x65, -1, 0x15, 0x10, 0x00, 0x00, 0x00 };
  59.  
  60. private static final String X86 = "x86";
  61.  
  62. private static final String SYSCALL_SPACE_NAME = "syscall";
  63.  
  64. private static final int SYSCALL_SPACE_LENGTH = 0x10000;
  65.  
  66. //this is the name of the userop (aka CALLOTHER) in the pcode translation of the
  67. //native "syscall" instruction
  68. private static final String SYSCALL_X64_CALLOTHER = "syscall";
  69.  
  70. //a set of names of all syscalls that do not return
  71. private static final Set<String> noreturnSyscalls = Set.of("exit", "exit_group");
  72.  
  73. //tests whether an instruction is making a system call
  74. private Predicate<Instruction> tester;
  75.  
  76. //register holding the syscall number
  77. private String syscallRegister;
  78.  
  79. //datatype archive containing signature of system calls
  80. private String datatypeArchiveName;
  81.  
  82. //file containing map from syscall numbers to syscall names
  83. //note that different architectures can have different system call numbers, even
  84. //if they're both Linux...
  85. private String syscallFileName;
  86.  
  87. //the type of overriding reference to apply
  88. private RefType overrideType;
  89.  
  90. //the calling convention to use for system calls (must be defined in the appropriate .cspec file)
  91. private String callingConvention;
  92.  
  93. @Override
  94. protected void run() throws Exception {
  95.  
  96. //determine whether the executable is 32 or 64 bit and set fields appropriately
  97. int size = 64;
  98. if (size == 64) {
  99. tester = ResolveX86orX64LinuxSyscallsScript2::checkX64Instruction;
  100. syscallRegister = "RAX";
  101. datatypeArchiveName = "generic_clib_64";
  102. syscallFileName = "x64_linux_syscall_numbers";
  103. overrideType = RefType.CALLOTHER_OVERRIDE_CALL;
  104. callingConvention = "syscall";
  105. }
  106. else {
  107. tester = ResolveX86orX64LinuxSyscallsScript2::checkX86Instruction;
  108. syscallRegister = "EAX";
  109. datatypeArchiveName = "generic_clib";
  110. syscallFileName = "x86_linux_syscall_numbers";
  111. overrideType = RefType.CALL_OVERRIDE_UNCONDITIONAL;
  112. callingConvention = "syscall";
  113. }
  114.  
  115. //get the space where the system calls live.
  116. //If it doesn't exist, create it.
  117. AddressSpace syscallSpace =
  118. currentProgram.getAddressFactory().getAddressSpace(SYSCALL_SPACE_NAME);
  119. if (syscallSpace == null) {
  120. //don't muck with address spaces if you don't have exclusive access to the program.
  121. if (!currentProgram.hasExclusiveAccess()) {
  122. popup("Must have exclusive access to " + currentProgram.getName() +
  123. " to run this script");
  124. return;
  125. }
  126. Address startAddr = currentProgram.getAddressFactory()
  127. .getAddressSpace(SpaceNames.OTHER_SPACE_NAME)
  128. .getAddress(0x0L);
  129. AddUninitializedMemoryBlockCmd cmd = new AddUninitializedMemoryBlockCmd(
  130. SYSCALL_SPACE_NAME, null, this.getClass().getName(), startAddr,
  131. SYSCALL_SPACE_LENGTH, true, true, true, false, true);
  132. if (!cmd.applyTo(currentProgram)) {
  133. popup("Failed to create " + SYSCALL_SPACE_NAME);
  134. return;
  135. }
  136. syscallSpace = currentProgram.getAddressFactory().getAddressSpace(SYSCALL_SPACE_NAME);
  137. }
  138. else {
  139. printf("AddressSpace %s found, continuing...\n", SYSCALL_SPACE_NAME);
  140. }
  141.  
  142. //get all of the functions that contain system calls
  143. //note that this will not find system call instructions that are not in defined functions
  144. Map<Function, Set<Address>> funcsToCalls = getSyscallsInFunctions(currentProgram, monitor);
  145.  
  146. if (funcsToCalls.isEmpty()) {
  147. popup("No system calls found (within defined functions)");
  148. return;
  149. }
  150.  
  151. //get the system call number at each callsite of a system call.
  152. //note that this is not guaranteed to succeed at a given system call call site -
  153. //it might be hard (or impossible) to determine a specific constant
  154. Map<Address, Long> addressesToSyscalls =
  155. resolveConstants(funcsToCalls, currentProgram, monitor);
  156.  
  157. if (addressesToSyscalls.isEmpty()) {
  158. popup("Couldn't resolve any syscall constants");
  159. return;
  160. }
  161.  
  162. //get the map from system call numbers to system call names
  163. //you might have to create this yourself!
  164. Map<Long, String> syscallNumbersToNames = getSyscallNumberMap();
  165.  
  166. //at each system call call site where a constant could be determined, create
  167. //the system call (if not already created), then add the appropriate overriding reference
  168. //use syscallNumbersToNames to name the created functions
  169. //if there's not a name corresponding to the constant use a default
  170. for (Entry<Address, Long> entry : addressesToSyscalls.entrySet()) {
  171. Address callSite = entry.getKey();
  172. Long offset = entry.getValue();
  173. Address callTarget = syscallSpace.getAddress(offset);
  174. Function callee = currentProgram.getFunctionManager().getFunctionAt(callTarget);
  175. if (callee == null) {
  176. String funcName = "syscall_" + String.format("%08X", offset);
  177. if (syscallNumbersToNames.get(offset) != null) {
  178. funcName = syscallNumbersToNames.get(offset);
  179. }
  180. callee = createFunction(callTarget, funcName);
  181. callee.setCallingConvention(callingConvention);
  182.  
  183. //check if the function name is one of the non-returning syscalls
  184. if (noreturnSyscalls.contains(funcName)) {
  185. callee.setNoReturn(true);
  186. }
  187. }
  188. Reference ref = currentProgram.getReferenceManager()
  189. .addMemoryReference(callSite, callTarget, overrideType, SourceType.USER_DEFINED,
  190. Reference.MNEMONIC);
  191. //overriding references must be primary to be active
  192. currentProgram.getReferenceManager().setPrimary(ref, true);
  193. }
  194.  
  195. //finally, open the appropriate data type archive and apply its function data types
  196. //to the new system call space, so that the system calls have the correct signatures
  197. AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(currentProgram);
  198. DataTypeManagerService service = mgr.getDataTypeManagerService();
  199. List<DataTypeManager> dataTypeManagers = new ArrayList<>();
  200. dataTypeManagers.add(service.openDataTypeArchive(datatypeArchiveName));
  201. dataTypeManagers.add(currentProgram.getDataTypeManager());
  202. ApplyFunctionDataTypesCmd cmd = new ApplyFunctionDataTypesCmd(dataTypeManagers,
  203. new AddressSet(syscallSpace.getMinAddress(), syscallSpace.getMaxAddress()),
  204. SourceType.USER_DEFINED, false, false);
  205. cmd.applyTo(currentProgram);
  206. }
  207.  
  208. //TODO: better error checking!
  209. private Map<Long, String> getSyscallNumberMap() {
  210. Map<Long, String> syscallMap = new HashMap<>();
  211. ResourceFile rFile = Application.findDataFileInAnyModule(syscallFileName);
  212. if (rFile == null) {
  213. popup("Error opening syscall number file, using default names");
  214. return syscallMap;
  215. }
  216. try (FileReader fReader = new FileReader(rFile.getFile(false));
  217. BufferedReader bReader = new BufferedReader(fReader)) {
  218. String line = null;
  219. while ((line = bReader.readLine()) != null) {
  220. //lines starting with # are comments
  221. if (!line.startsWith("#")) {
  222. String[] parts = line.trim().split(" ");
  223. Long number = Long.parseLong(parts[0]);
  224. syscallMap.put(number, parts[1]);
  225. }
  226. }
  227. }
  228. catch (IOException e) {
  229. Msg.showError(this, null, "Error reading syscall map file", e.getMessage(), e);
  230. }
  231. return syscallMap;
  232. }
  233.  
  234. /**
  235. * Scans through all of the functions defined in {@code program} and returns
  236. * a map which takes a function to the set of address in its body which contain
  237. * system calls
  238. * @param program program containing functions
  239. * @param tMonitor monitor
  240. * @return map function -> addresses in function containing syscalls
  241. * @throws CancelledException if the user cancels
  242. */
  243. private Map<Function, Set<Address>> getSyscallsInFunctions(Program program,
  244. TaskMonitor tMonitor) throws CancelledException {
  245. Map<Function, Set<Address>> funcsToCalls = new HashMap<>();
  246. for (Function func : program.getFunctionManager().getFunctionsNoStubs(true)) {
  247. tMonitor.checkCancelled();
  248. for (Instruction inst : program.getListing().getInstructions(func.getBody(), true)) {
  249. if (tester.test(inst)) {
  250. Set<Address> callSites = funcsToCalls.get(func);
  251. if (callSites == null) {
  252. callSites = new HashSet<>();
  253. funcsToCalls.put(func, callSites);
  254. }
  255. callSites.add(inst.getAddress());
  256. }
  257. }
  258. }
  259. return funcsToCalls;
  260. }
  261.  
  262. /**
  263. * Uses the symbolic propogator to attempt to determine the constant value in
  264. * the syscall register at each system call instruction
  265. *
  266. * @param funcsToCalls map from functions containing syscalls to address in each function of
  267. * the system call
  268. * @param program containing the functions
  269. * @return map from addresses of system calls to system call numbers
  270. * @throws CancelledException if the user cancels
  271. */
  272. private Map<Address, Long> resolveConstants(Map<Function, Set<Address>> funcsToCalls,
  273. Program program, TaskMonitor tMonitor) throws CancelledException {
  274. Map<Address, Long> addressesToSyscalls = new HashMap<>();
  275. Register syscallReg = program.getLanguage().getRegister(syscallRegister);
  276. for (Function func : funcsToCalls.keySet()) {
  277. Address start = func.getEntryPoint();
  278. ContextEvaluator eval = new ConstantPropagationContextEvaluator(monitor, true);
  279. SymbolicPropogator symEval = new SymbolicPropogator(program);
  280. symEval.flowConstants(start, func.getBody(), eval, true, tMonitor);
  281. for (Address callSite : funcsToCalls.get(func)) {
  282. Value val = symEval.getRegisterValue(callSite, syscallReg);
  283. if (val == null) {
  284. createBookmark(callSite, "System Call",
  285. "Couldn't resolve value of " + syscallReg);
  286. printf("Couldn't resolve value of " + syscallReg + " at " + callSite + "\n");
  287. continue;
  288. }
  289. addressesToSyscalls.put(callSite, val.getValue());
  290. }
  291. }
  292. return addressesToSyscalls;
  293. }
  294.  
  295. /**
  296. * Checks whether an x86 native instruction is a system call
  297. * @param inst instruction to check
  298. * @return true precisely when the instruction is a system call
  299. */
  300. private static boolean checkX86Instruction(Instruction inst) {
  301. try {
  302. return Arrays.equals(x86_bytes, inst.getBytes());
  303. }
  304. catch (MemoryAccessException e) {
  305. Msg.info(ResolveX86orX64LinuxSyscallsScript2.class,
  306. "MemoryAccessException at " + inst.getAddress().toString());
  307. return false;
  308. }
  309. }
  310.  
  311. /**
  312. * Checks whether an x64 instruction is a system call
  313. * @param inst instruction to check
  314. * @return true precisely when the instruction is a system call
  315. */
  316. private static boolean checkX64Instruction(Instruction inst) {
  317. boolean retVal = false;
  318. for (PcodeOp op : inst.getPcode()) {
  319. if (op.getOpcode() == PcodeOp.CALLOTHER) {
  320. int index = (int) op.getInput(0).getOffset();
  321. if (inst.getProgram()
  322. .getLanguage()
  323. .getUserDefinedOpName(index)
  324. .equals(SYSCALL_X64_CALLOTHER)) {
  325. retVal = true;
  326. }
  327. }
  328. }
  329. return retVal;
  330. }
  331.  
  332. }
  333.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement