mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-10 06:27:59 -05:00
GP-6084: Fix register writing for lldb (schema and code)
Some work toward dbgeng as well, but not yet resolved.
This commit is contained in:
@@ -26,7 +26,7 @@ from ghidratrace.client import (MethodRegistry, ParamDesc, Address,
|
||||
from pybag import pydbg # type: ignore
|
||||
from pybag.dbgeng import core as DbgEng, exception # type: ignore
|
||||
|
||||
from . import util, commands
|
||||
from . import arch, util, commands
|
||||
|
||||
|
||||
REGISTRY = MethodRegistry(ThreadPoolExecutor(
|
||||
@@ -846,8 +846,11 @@ def write_reg(frame: StackFrame, name: str, value: bytes) -> None:
|
||||
"""Write a register."""
|
||||
f = find_frame_by_obj(frame)
|
||||
util.select_frame(f.FrameNumber)
|
||||
nproc = pydbg.selected_process()
|
||||
dbg().reg._set_register(name, value)
|
||||
nproc = util.selected_process()
|
||||
trace: Trace[commands.Extra] = frame.trace
|
||||
rv = trace.extra.require_rm().map_value_back(nproc, name, value)
|
||||
rval = int.from_bytes(rv.value, signed=False)
|
||||
dbg().reg._set_register(name, rval)
|
||||
|
||||
|
||||
@REGISTRY.method(display='Refresh Events (custom)', condition=util.dbg.IS_TRACE)
|
||||
|
||||
@@ -309,6 +309,9 @@
|
||||
</schema>
|
||||
<schema name="RegisterBank" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
<attribute schema="Register" />
|
||||
</schema>
|
||||
<schema name="Register">
|
||||
<interface name="Register" />
|
||||
</schema>
|
||||
</context>
|
||||
@@ -270,7 +270,10 @@
|
||||
</schema>
|
||||
<schema name="RegisterBank" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
<attribute schema="Register" />
|
||||
</schema>
|
||||
<schema name="Register">
|
||||
<interface name="Register" />
|
||||
</schema>
|
||||
|
||||
<schema name="ExdiProcessContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
|
||||
@@ -225,6 +225,9 @@
|
||||
<schema name="RegisterValueContainer" attributeResync="NEVER">
|
||||
<interface name="RegisterContainer" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
<attribute schema="Register" />
|
||||
</schema>
|
||||
<schema name="Register">
|
||||
<interface name="Register" />
|
||||
</schema>
|
||||
</context>
|
||||
@@ -308,6 +308,8 @@ class DefaultRegisterMapper(object):
|
||||
|
||||
def map_value_back(self, proc: lldb.SBProcess, name: str,
|
||||
value: bytes) -> RegVal:
|
||||
if self.byte_order == 'little':
|
||||
value = bytes(reversed(value))
|
||||
return RegVal(self.map_name_back(proc, name), value)
|
||||
|
||||
|
||||
|
||||
@@ -106,6 +106,10 @@ def find_proc_by_modules_obj(object: TraceObject) -> lldb.SBProcess:
|
||||
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
|
||||
|
||||
|
||||
def find_proc_by_frame(object: TraceObject) -> lldb.SBProcess:
|
||||
return find_proc_by_pattern(object, FRAME_PATTERN, "a StaclFrame")
|
||||
|
||||
|
||||
def find_thread_by_num(proc: lldb.SBThread, tnum: int) -> lldb.SBThread:
|
||||
for t in proc.threads:
|
||||
if t.GetThreadID() == tnum:
|
||||
@@ -731,11 +735,22 @@ def write_mem(process: Process, address: Address, data: bytes) -> None:
|
||||
@REGISTRY.method()
|
||||
def write_reg(frame: StackFrame, name: str, value: bytes) -> None:
|
||||
"""Write a register."""
|
||||
proc = find_proc_by_frame(frame)
|
||||
util.get_debugger().SetSelectedTarget(proc.target)
|
||||
f = find_frame_by_obj(frame)
|
||||
f.select()
|
||||
proc = lldb.selected_process()
|
||||
mname, mval = frame.trace.extra.require_rm().map_value_back(proc, name, value)
|
||||
size = int(lldb.parse_and_eval(f'sizeof(${mname})'))
|
||||
arr = '{' + ','.join(str(b) for b in mval) + '}'
|
||||
exec_convert_errors(
|
||||
f'expr ((unsigned char[{size}])${mname}) = {arr};')
|
||||
exec_convert_errors(f'frame select {f.idx}')
|
||||
rv = frame.trace.extra.require_rm().map_value_back(proc, name, value)
|
||||
reg = f.registers[0].GetChildMemberWithName(name)
|
||||
error = lldb.SBError()
|
||||
data = lldb.SBData()
|
||||
tgt = util.get_target()
|
||||
for b in rv.value:
|
||||
bv = tgt.EvaluateExpression(f"(char){b}")
|
||||
if bv.error.fail:
|
||||
raise Exception(bv.error.description)
|
||||
if not data.Append(bv.GetData()):
|
||||
raise Exception(f"Could not build data for register value {rv.value}")
|
||||
if not reg.SetData(data, error):
|
||||
raise Exception(error.description)
|
||||
with commands.open_tracked_tx(f'Write Register {name}'):
|
||||
exec_convert_errors('ghidra trace putreg')
|
||||
|
||||
@@ -260,6 +260,9 @@
|
||||
</schema>
|
||||
<schema name="RegisterBank" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
<attribute schema="Register" />
|
||||
</schema>
|
||||
<schema name="Register">
|
||||
<interface name="Register" />
|
||||
</schema>
|
||||
</context>
|
||||
@@ -0,0 +1,103 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.service.tracermi;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||
import ghidra.app.plugin.core.debug.service.tracermi.TraceRmiTarget.FoundRegister;
|
||||
import ghidra.app.services.DebuggerControlService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.debug.api.tracermi.TraceRmiConnection;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.target.schema.SchemaContext;
|
||||
import ghidra.trace.model.target.schema.XmlSchemaContext;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
public class TraceRmiTargetTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
|
||||
class MyTraceRmiConnection extends TestTraceRmiConnection {
|
||||
|
||||
@Override
|
||||
protected DebuggerTraceManagerService getTraceManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerControlService getControlService() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForRegistersInGroups() throws Exception {
|
||||
SchemaContext ctx = XmlSchemaContext.deserialize("""
|
||||
<context>
|
||||
<schema name="root">
|
||||
<interface name="Aggregate" />
|
||||
<attribute name="Threads" schema="ThreadContainer" />
|
||||
</schema>
|
||||
<schema name="ThreadContainer" canonical="yes">
|
||||
<element schema="Thread" />
|
||||
</schema>
|
||||
<schema name="Thread">
|
||||
<interface name="Thread" />
|
||||
<interface name="Aggregate" />
|
||||
<attribute name="Registers" schema="RegisterValueContainer" />
|
||||
</schema>
|
||||
<schema name="RegisterValueContainer">
|
||||
<interface name="RegisterContainer" />
|
||||
<attribute name="General Purpose" schema="RegisterBank" />
|
||||
</schema>
|
||||
<schema name="RegisterBank" canonical="yes">
|
||||
<attribute schema="Register" />
|
||||
<interface name="Aggregate" />
|
||||
</schema>
|
||||
<schema name="Register">
|
||||
<interface name="Register" />
|
||||
</schema>
|
||||
</context>
|
||||
""");
|
||||
|
||||
PluginTool tool = env.getTool();
|
||||
createTrace("x86:LE:64:default");
|
||||
Register regRax = tb.reg("RAX");
|
||||
try (Transaction tx = tb.startTransaction()) {
|
||||
tb.createRootObject(ctx, "root");
|
||||
TraceObject objRax = tb.trace.getObjectManager()
|
||||
.createObject(KeyPath.parse("Threads[1].Registers.General Purpose.RAX"));
|
||||
objRax.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
}
|
||||
|
||||
try (TraceRmiConnection cx = new MyTraceRmiConnection()) {
|
||||
TraceRmiTarget target = new TraceRmiTarget(tool, cx, tb.trace);
|
||||
|
||||
TraceThread thread = tb.obj("Threads[1]").queryInterface(TraceThread.class);
|
||||
FoundRegister found = target.findRegister(thread, 0, regRax);
|
||||
assertEquals("Threads[1].Registers.General Purpose.RAX",
|
||||
found.value().getCanonicalPath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user