From 3b4fefe3ba18e6b4d1b3fdcc60975f1350048600 Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:36:53 -0400 Subject: [PATCH] GP-5034: post-review GP-5034: oops GP-5034: mods for android etc GP-5034: running android debugger GP-5034: running android debugger GP-5034: start of dalvik changes GP-5034: start of Dalvik changes GP-5034: android-native launcher GP-5034: added mips:3000 GP-5034: fix for arch error --- .../src/main/py/src/ghidragdb/arch.py | 27 +++--- .../data/debugger-launchers/android-lldb.sh | 82 +++++++++++++++++ .../src/main/py/src/ghidralldb/arch.py | 35 ++++--- .../java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java | 9 +- .../ghidra/dbg/jdi/rmi/jpda/JdiCommands.java | 92 ++++++++++++++----- .../ghidra/dbg/jdi/rmi/jpda/JdiManager.java | 8 +- .../ghidra/dbg/jdi/rmi/jpda/JdiMethods.java | 26 ++++++ 7 files changed, 221 insertions(+), 58 deletions(-) create mode 100755 Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py index 3c115c99d7..0769aff7fb 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py @@ -1,17 +1,17 @@ ## ### -# IP: GHIDRA -# -# 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. +# IP: GHIDRA +# +# 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. ## from ghidratrace.client import Address, RegVal @@ -61,6 +61,7 @@ language_map = { 'm68k:68020': ['68000:BE:32:MC68020'], 'm68k:68030': ['68000:BE:32:MC68030'], 'm9s12x': ['HCS-12:BE:24:default', 'HCS-12X:BE:24:default'], + 'mips:3000': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], 'mips:4000': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], 'mips:5000': ['MIPS:BE:64:64-32addr', 'MIPS:BE:64:default', 'MIPS:LE:64:64-32addr', 'MIPS:LE:64:default'], 'mips:micromips': ['MIPS:BE:32:micro'], diff --git a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh new file mode 100755 index 0000000000..679cf4ca0e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/android-lldb.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +## ### +# IP: GHIDRA +# +# 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. +## +#@title android lldb +#@image-opt arg:1 +#@desc +#@desc

Launch with local lldb and connect to a stub (e.g., gdbserver)

+#@desc

+#@desc This will start lldb on the local system and then use it to connect to the remote system. +#@desc For setup instructions, press F1. +#@desc

+#@desc +#@menu-group remote +#@icon icon.debugger +#@help TraceRmiLauncherServicePlugin#lldb_remote +#@enum StartCmd:str "process launch" "process launch --stop-at-entry" +#@arg :file "Image" "The target binary executable image" +#@args "Arguments" "Command-line arguments to pass to the target" +#@env OPT_HOST:str="localhost" "Host" "The hostname of the target" +#@env OPT_PORT:str="9999" "Port" "The host's listening port" +#@env OPT_ARCH:str="" "Architecture" "Target architecture override" +#@env OPT_LLDB_PATH:file="lldb" "lldb command" "The path to lldb on the local system. Omit the full path to resolve using the system PATH." +#@env OPT_START_CMD:StartCmd="process launch" "Run command" "The lldb command to actually run the target." + +if [ -d ${GHIDRA_HOME}/ghidra/.git ] +then + export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-agent-lldb/build/pypkg/src:$PYTHONPATH + export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-rmi-trace/build/pypkg/src:$PYTHONPATH +elif [ -d ${GHIDRA_HOME}/.git ] +then + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-agent-lldb/build/pypkg/src:$PYTHONPATH + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/build/pypkg/src:$PYTHONPATH +else + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-agent-lldb/pypkg/src:$PYTHONPATH + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/pypkg/src:$PYTHONPATH +fi + +target_image="$1" +shift +target_args="$@" + +if [ -z "$target_args" ] +then + argspart= +else + argspart=-o "settings set target.run-args $target_args" +fi + +if [ -z "$OPT_ARCH" ] +then + archcmd= +else + archcmd=-o "settings set target.default-arch $OPT_ARCH" +fi + +"$OPT_LLDB_PATH" \ + -o "version" \ + -o "script import ghidralldb" \ + -o "platform select remote-android" \ + -o "platform connect connect://$OPT_HOST:$OPT_PORT" \ + $archcmd \ + -o "target create \"$target_image\"" \ + $argspart \ + -o "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \ + -o "ghidra trace start" \ + -o "ghidra trace sync-enable" \ + -o "ghidra trace sync-synth-stopped" \ + -o "$OPT_START_CMD" + diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py index eca5ae3f04..e0fb5a9f07 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py @@ -1,17 +1,17 @@ ## ### -# IP: GHIDRA -# -# 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. +# IP: GHIDRA +# +# 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. ## from ghidratrace.client import Address, RegVal import lldb @@ -226,14 +226,11 @@ def compute_ghidra_compiler(lang): ) if len(matched_lang) == 0: return 'default' + comp_map = compiler_map[matched_lang[0]] osabi = get_osabi() - matched_osabi = sorted( - (l for l in comp_map if l in osabi), - key=lambda l: comp_map[l] - ) - if len(matched_osabi) > 0: - return comp_map[matched_osabi[0]] + if osabi in comp_map: + return comp_map[osabi] if None in comp_map: return comp_map[None] return 'default' diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java index 27d444eb98..c69e2f2d65 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiArch.java @@ -25,13 +25,18 @@ import ghidra.program.util.DefaultLanguageService; public class JdiArch { + private JdiManager manager; private LanguageID langID; private Language language; private final LanguageService languageService = DefaultLanguageService.getLanguageService(); + public JdiArch(JdiManager manager) { + this.manager = manager; + } + public String getArch() { - Map env = new HashMap<>(System.getenv()); + Map env = new HashMap<>(manager.getEnv()); String arch = "JVM"; if (env.containsKey("OPT_ARCH")) { arch = env.get("OPT_ARCH"); @@ -44,7 +49,7 @@ public class JdiArch { } public String getOSABI() { - Map env = new HashMap<>(System.getenv()); + Map env = new HashMap<>(manager.getEnv()); String arch = "JVM"; if (env.containsKey("OPT_ARCH")) { arch = env.get("OPT_ARCH"); diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java index 75fff119cb..e635a76a3d 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java @@ -588,6 +588,9 @@ public class JdiCommands { int ireg = 0; String r = regNames[0]; Register register = lang.getRegister(r); + if (register == null) { + register = fabricatePcRegister(lang, r); + } keys.add(manager.key(r)); Location loc = frame.location(); Address addr = putRegister(ppath, r, loc); @@ -596,6 +599,9 @@ public class JdiCommands { r = regNames[1]; register = lang.getRegister(r); + if (register == null) { + register = fabricatePcRegister(lang, r); + } keys.add(manager.key(r)); ThreadReference thread = frame.thread(); Location ploc = null; @@ -648,6 +654,9 @@ public class JdiCommands { JdiArch arch = manager.getArch(); Language lang = arch.getLanguage(); Register register = lang.getRegister(name); + if (register == null) { + register = fabricatePcRegister(lang, name); + } RegisterValue rv = new RegisterValue(register, addr.getOffsetAsBigInteger()); RegisterValue mapped = mapper.mapValue(name, rv); Address regAddr = addr.getNewAddress(mapped.getUnsignedValue().longValue()); @@ -660,6 +669,11 @@ public class JdiCommands { return addr; } + private Register fabricatePcRegister(Language lang, String name) { + int size = lang.getAddressFactory().getDefaultAddressSpace().getSize(); + return new Register(name, name, null, size, lang.isBigEndian(), Register.TYPE_PC); + } + public void putMem(Address address, long length, boolean create) { MemoryMapper mapper = state.trace.memoryMapper; Address mappedAddress = mapper.map(address); @@ -754,16 +768,22 @@ public class JdiCommands { String rpath = createObject(path + ".Relations"); insertObject(rpath); - ModuleReference module = reftype.module(); - String moduleName = module.name(); - if (moduleName == null) { - moduleName = ""; + try { + ModuleReference module = reftype.module(); + String moduleName = module.name(); + if (moduleName == null) { + moduleName = ""; + } + if (moduleName.contains(".")) { + moduleName = "\"" + moduleName + "\""; + } + String mrpath = createObject(module, moduleName, rpath + ".ModuleRef"); + insertObject(mrpath); } - if (moduleName.contains(".")) { - moduleName = "\"" + moduleName + "\""; + catch (UnsupportedOperationException e) { + //Msg.info(this, e.getMessage()); } - String mrpath = createObject(module, moduleName, rpath + ".ModuleRef"); - insertObject(mrpath); + if (reftype instanceof ArrayType at) { putArrayTypeDetails(rpath, at); } @@ -823,9 +843,14 @@ public class JdiCommands { AddressRange range = manager.putAddressRange(reftype, bounds); setValue(path, ATTR_RANGE, range); - setValue(path, ATTR_COUNT, reftype.constantPoolCount()); - range = manager.getPoolAddressRange(reftype, getSize(reftype) - 1); - setValue(path, ATTR_RANGE_CP, range); + try { + setValue(path, ATTR_COUNT, reftype.constantPoolCount()); + range = manager.getPoolAddressRange(reftype, getSize(reftype) - 1); + setValue(path, ATTR_RANGE_CP, range); + } + catch (UnsupportedOperationException e) { + // Ignore + } try { putMem(range.getMinAddress(), range.getLength(), true); } @@ -1157,11 +1182,16 @@ public class JdiCommands { VirtualMachine vm = manager.getJdi().getCurrentVM(); String ppath = getPath(vm) + ".ModuleRefs"; Set keys = new HashSet<>(); - List modules = vm.allModules(); - for (ModuleReference ref : modules) { - keys.add(manager.key(ref.name())); - String mpath = createObject(ref, ref.name(), ppath); - insertObject(mpath); + try { + List modules = vm.allModules(); + for (ModuleReference ref : modules) { + keys.add(manager.key(ref.name())); + String mpath = createObject(ref, ref.name(), ppath); + insertObject(mpath); + } + } + catch (UnsupportedOperationException e) { + // Msg.info(this, e.getMessage()); } retainKeys(ppath, keys); } @@ -1380,11 +1410,16 @@ public class JdiCommands { public void putMethodContainer(String path, ReferenceType reftype) { boolean scope = manager.getScope(reftype); - List methods = scope ? reftype.allMethods() : reftype.methods(); Set keys = new HashSet<>(); - for (Method m : methods) { - keys.add(manager.key(m.name())); - putMethod(path, m); + try { + List methods = scope ? reftype.allMethods() : reftype.methods(); + for (Method m : methods) { + keys.add(manager.key(m.name())); + putMethod(path, m); + } + } + catch (Exception e) { + Msg.info(this, e.getMessage()); } retainKeys(path, keys); } @@ -1477,7 +1512,10 @@ public class JdiCommands { String tgpath = createObject(path + ".ThreadGroups"); String tpath = createObject(path + ".Threads"); Event currentEvent = jdi.getCurrentEvent(); - String shortName = vm.name().substring(0, vm.name().indexOf(" ")); + String shortName = vm.name(); + if (shortName.contains(" ")) { + shortName = vm.name().substring(0, vm.name().indexOf(" ")); + } String display = currentEvent == null ? shortName : shortName + " [" + currentEvent + "]"; setValue(path, ATTR_DISPLAY, display); setValue(path, ATTR_ARCH, vm.name()); @@ -1640,7 +1678,12 @@ public class JdiCommands { } createLink(location, "Method", method); createLink(location, "DeclaringType", location.declaringType()); - createLink(location, "ModuleRef", location.declaringType().module()); + try { + createLink(location, "ModuleRef", location.declaringType().module()); + } + catch (UnsupportedOperationException e) { + // IGNORE + } } private boolean isLoaded(Location location) { @@ -2249,7 +2292,10 @@ public class JdiCommands { } if (obj instanceof VirtualMachine vm) { Event currentEvent = jdi.getCurrentEvent(); - String shortName = vm.name().substring(0, vm.name().indexOf(" ")); + String shortName = vm.name(); + if (shortName.contains(" ")) { + shortName = vm.name().substring(0, vm.name().indexOf(" ")); + } name = currentEvent == null ? shortName : shortName + " [" + currentEvent + "]"; } setValue(path, ATTR_ACCESSIBLE, suspended); diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java index ce7ba8a173..76dfcd958f 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiManager.java @@ -93,9 +93,11 @@ public class JdiManager { private final Map returnStatusMap = new HashMap<>(); final TargetObjectSchema rootSchema; + private Map env; public JdiManager(JdiManagerImpl manager, Map env) { this(manager); + this.env = env; commands.ghidraTraceConnect(env.get("GHIDRA_TRACE_RMI_ADDR")); commands.ghidraTraceStart(env.get("OPT_TARGET_CLASS")); } @@ -106,7 +108,7 @@ public class JdiManager { defaultRange = new AddressRangeImpl(start, start.add(BLOCK_SIZE - 1)); rootSchema = RmiClient.loadSchema("jdi_schema.xml", "Debugger"); - arch = new JdiArch(); + arch = new JdiArch(this); commands = new JdiCommands(this); // Must precede methods/hooks methods = new JdiMethods(this, commands); hooks = new JdiHooks(this, commands); @@ -137,6 +139,10 @@ public class JdiManager { return commands.state.client; } + public Map getEnv() { + return env; + } + public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) { String action = name; String display = name; diff --git a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java index 1ed7dabdfc..e226cd61bd 100644 --- a/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java +++ b/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiMethods.java @@ -330,6 +330,20 @@ public class JdiMethods implements RmiMethods { } } + @TraceMethod(display = "Load class") + public boolean find_canonical_class( + @Param( + schema = "CanonicalReferenceTypeContainer", + description = "Container", + display = "Container", + name = "container") RmiTraceObject obj, + @Param( + description = "Class to open", + display = "Class", + name = "find") String targetClass) { + return find_class(obj, targetClass); + } + @TraceMethod(display = "Load class") public boolean find_class( @Param( @@ -1428,6 +1442,12 @@ public class JdiMethods implements RmiMethods { cmds.putEvents(); } + @TraceMethod(action = "toggle", display = "Toggle scope") + public void toggle_scope_canonical_methods( + @Param(schema = "CanonicalMethodContainer", name = "container") RmiTraceObject obj) { + toggle_scope_methods(obj); + } + @TraceMethod(action = "toggle", display = "Toggle scope") public void toggle_scope_methods( @Param(schema = "MethodContainer", name = "container") RmiTraceObject obj) { @@ -1437,6 +1457,12 @@ public class JdiMethods implements RmiMethods { refresh_methods(obj); } + @TraceMethod(action = "toggle", display = "Toggle scope") + public void toggle_scope_canonical_fields( + @Param(schema = "CanonicalFieldContainer", name = "container") RmiTraceObject obj) { + toggle_scope_fields(obj); + } + @TraceMethod(action = "toggle", display = "Toggle scope") public void toggle_scope_fields( @Param(schema = "FieldContainer", name = "container") RmiTraceObject obj) {