mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-4209: GhidraTime-MSTTD integration. Type hints for (most) Python agents.
This commit is contained in:
parent
deb49d5322
commit
21a1602579
93 changed files with 6453 additions and 4118 deletions
|
@ -44,10 +44,15 @@ public abstract class AbstractTraceRmiConnection implements TraceRmiConnection {
|
|||
protected void doActivate(TraceObject object, Trace trace, TraceSnapshot snapshot) {
|
||||
DebuggerCoordinates coords = getTraceManager().getCurrent();
|
||||
if (coords.getTrace() != trace) {
|
||||
coords = DebuggerCoordinates.NOWHERE;
|
||||
coords = DebuggerCoordinates.NOWHERE.trace(trace);
|
||||
}
|
||||
if (snapshot != null && followsPresent(trace)) {
|
||||
coords = coords.snap(snapshot.getKey());
|
||||
if (snapshot.getKey() > 0 || snapshot.getSchedule() == null) {
|
||||
coords = coords.snap(snapshot.getKey());
|
||||
}
|
||||
else {
|
||||
coords = coords.time(snapshot.getSchedule());
|
||||
}
|
||||
}
|
||||
DebuggerCoordinates finalCoords = object == null ? coords : coords.object(object);
|
||||
Swing.runLater(() -> {
|
||||
|
@ -68,5 +73,4 @@ public abstract class AbstractTraceRmiConnection implements TraceRmiConnection {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import ghidra.rmi.trace.TraceRmi.*;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
class OpenTrace implements ValueDecoder {
|
||||
|
@ -79,9 +80,16 @@ class OpenTrace implements ValueDecoder {
|
|||
trace.release(consumer);
|
||||
}
|
||||
|
||||
public TraceSnapshot createSnapshot(Snap snap, String description) {
|
||||
TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap.getSnap(), true);
|
||||
snapshot.setDescription(description);
|
||||
public TraceSnapshot createSnapshot(long snap) {
|
||||
TraceSnapshot snapshot = trace.getTimeManager().getSnapshot(snap, true);
|
||||
return this.lastSnapshot = snapshot;
|
||||
}
|
||||
|
||||
public TraceSnapshot createSnapshot(TraceSchedule schedule) {
|
||||
if (schedule.isSnapOnly()) {
|
||||
return createSnapshot(schedule.getSnap());
|
||||
}
|
||||
TraceSnapshot snapshot = trace.getTimeManager().findScratchSnapshot(schedule);
|
||||
return this.lastSnapshot = snapshot;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,12 +61,13 @@ import ghidra.trace.model.target.path.*;
|
|||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||
import ghidra.trace.model.target.schema.XmlSchemaContext;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.DuplicateFileException;
|
||||
|
||||
public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
||||
public static final String VERSION = "11.3";
|
||||
public static final String VERSION = "11.4";
|
||||
|
||||
protected static class VersionMismatchError extends TraceRmiError {
|
||||
public VersionMismatchError(String remote) {
|
||||
|
@ -740,77 +741,43 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
}
|
||||
|
||||
protected static Value makeValue(Object value) {
|
||||
if (value instanceof Void) {
|
||||
return Value.newBuilder().setNullValue(Null.getDefaultInstance()).build();
|
||||
}
|
||||
if (value instanceof Boolean b) {
|
||||
return Value.newBuilder().setBoolValue(b).build();
|
||||
}
|
||||
if (value instanceof Byte b) {
|
||||
return Value.newBuilder().setByteValue(b).build();
|
||||
}
|
||||
if (value instanceof Character c) {
|
||||
return Value.newBuilder().setCharValue(c).build();
|
||||
}
|
||||
if (value instanceof Short s) {
|
||||
return Value.newBuilder().setShortValue(s).build();
|
||||
}
|
||||
if (value instanceof Integer i) {
|
||||
return Value.newBuilder().setIntValue(i).build();
|
||||
}
|
||||
if (value instanceof Long l) {
|
||||
return Value.newBuilder().setLongValue(l).build();
|
||||
}
|
||||
if (value instanceof String s) {
|
||||
return Value.newBuilder().setStringValue(s).build();
|
||||
}
|
||||
if (value instanceof boolean[] ba) {
|
||||
return Value.newBuilder()
|
||||
return switch (value) {
|
||||
case Void v -> Value.newBuilder().setNullValue(Null.getDefaultInstance()).build();
|
||||
case Boolean b -> Value.newBuilder().setBoolValue(b).build();
|
||||
case Byte b -> Value.newBuilder().setByteValue(b).build();
|
||||
case Character c -> Value.newBuilder().setCharValue(c).build();
|
||||
case Short s -> Value.newBuilder().setShortValue(s).build();
|
||||
case Integer i -> Value.newBuilder().setIntValue(i).build();
|
||||
case Long l -> Value.newBuilder().setLongValue(l).build();
|
||||
case String s -> Value.newBuilder().setStringValue(s).build();
|
||||
case boolean[] ba -> Value.newBuilder()
|
||||
.setBoolArrValue(
|
||||
BoolArr.newBuilder().addAllArr(Arrays.asList(ArrayUtils.toObject(ba))))
|
||||
.build();
|
||||
}
|
||||
if (value instanceof byte[] ba) {
|
||||
return Value.newBuilder().setBytesValue(ByteString.copyFrom(ba)).build();
|
||||
}
|
||||
if (value instanceof char[] ca) {
|
||||
return Value.newBuilder().setCharArrValue(new String(ca)).build();
|
||||
}
|
||||
if (value instanceof short[] sa) {
|
||||
return Value.newBuilder()
|
||||
case byte[] ba -> Value.newBuilder().setBytesValue(ByteString.copyFrom(ba)).build();
|
||||
case char[] ca -> Value.newBuilder().setCharArrValue(new String(ca)).build();
|
||||
case short[] sa -> Value.newBuilder()
|
||||
.setShortArrValue(ShortArr.newBuilder()
|
||||
.addAllArr(
|
||||
Stream.of(ArrayUtils.toObject(sa)).map(s -> (int) s).toList()))
|
||||
.build();
|
||||
}
|
||||
if (value instanceof int[] ia) {
|
||||
return Value.newBuilder()
|
||||
case int[] ia -> Value.newBuilder()
|
||||
.setIntArrValue(
|
||||
IntArr.newBuilder().addAllArr(IntStream.of(ia).mapToObj(i -> i).toList()))
|
||||
.build();
|
||||
}
|
||||
if (value instanceof long[] la) {
|
||||
return Value.newBuilder()
|
||||
case long[] la -> Value.newBuilder()
|
||||
.setLongArrValue(
|
||||
LongArr.newBuilder().addAllArr(LongStream.of(la).mapToObj(l -> l).toList()))
|
||||
.build();
|
||||
}
|
||||
if (value instanceof String[] sa) {
|
||||
return Value.newBuilder()
|
||||
case String[] sa -> Value.newBuilder()
|
||||
.setStringArrValue(StringArr.newBuilder().addAllArr(List.of(sa)))
|
||||
.build();
|
||||
}
|
||||
if (value instanceof Address a) {
|
||||
return Value.newBuilder().setAddressValue(makeAddr(a)).build();
|
||||
}
|
||||
if (value instanceof AddressRange r) {
|
||||
return Value.newBuilder().setRangeValue(makeAddrRange(r)).build();
|
||||
}
|
||||
if (value instanceof TraceObject o) {
|
||||
return Value.newBuilder().setChildDesc(makeObjDesc(o)).build();
|
||||
}
|
||||
throw new AssertionError(
|
||||
"Cannot encode value: " + value + "(type=" + value.getClass() + ")");
|
||||
case Address a -> Value.newBuilder().setAddressValue(makeAddr(a)).build();
|
||||
case AddressRange r -> Value.newBuilder().setRangeValue(makeAddrRange(r)).build();
|
||||
case TraceObject o -> Value.newBuilder().setChildDesc(makeObjDesc(o)).build();
|
||||
default -> throw new AssertionError(
|
||||
"Cannot encode value: " + value + "(type=" + value.getClass() + ")");
|
||||
};
|
||||
}
|
||||
|
||||
protected static MethodArgument makeArgument(String name, Object value) {
|
||||
|
@ -958,8 +925,9 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
dis.applyTo(open.trace.getFixedProgramView(snap), monitor);
|
||||
}
|
||||
|
||||
AddressSetView result = dis.getDisassembledAddressSet();
|
||||
return ReplyDisassemble.newBuilder()
|
||||
.setLength(dis.getDisassembledAddressSet().getNumAddresses())
|
||||
.setLength(result == null ? 0 : result.getNumAddresses())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -1180,13 +1148,21 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
|
||||
protected ReplySnapshot handleSnapshot(RequestSnapshot req) {
|
||||
OpenTrace open = requireOpenTrace(req.getOid());
|
||||
TraceSnapshot snapshot = open.createSnapshot(req.getSnap(), req.getDescription());
|
||||
TraceSnapshot snapshot = switch (req.getTimeCase()) {
|
||||
case TIME_NOT_SET -> throw new TraceRmiError("snap or time required");
|
||||
case SNAP -> open.createSnapshot(req.getSnap().getSnap());
|
||||
case SCHEDULE -> open
|
||||
.createSnapshot(TraceSchedule.parse(req.getSchedule().getSchedule()));
|
||||
};
|
||||
snapshot.setDescription(req.getDescription());
|
||||
if (!"".equals(req.getDatetime())) {
|
||||
Instant instant =
|
||||
DateTimeFormatter.ISO_INSTANT.parse(req.getDatetime()).query(Instant::from);
|
||||
snapshot.setRealTime(instant.toEpochMilli());
|
||||
}
|
||||
return ReplySnapshot.getDefaultInstance();
|
||||
return ReplySnapshot.newBuilder()
|
||||
.setSnap(Snap.newBuilder().setSnap(snapshot.getKey()))
|
||||
.build();
|
||||
}
|
||||
|
||||
protected ReplyStartTx handleStartTx(RequestStartTx req) {
|
||||
|
|
|
@ -61,11 +61,11 @@ import ghidra.trace.model.target.schema.*;
|
|||
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema.MinimalSchemaContext;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule.ScheduleForm;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class TraceRmiTarget extends AbstractTarget {
|
||||
|
||||
class TraceRmiActionEntry implements ActionEntry {
|
||||
private final RemoteMethod method;
|
||||
private final Map<String, Object> args;
|
||||
|
@ -169,6 +169,48 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
}
|
||||
}
|
||||
|
||||
protected ScheduleForm getSupportedTimeFormByMethod(TraceObject obj) {
|
||||
KeyPath path = obj.getCanonicalPath();
|
||||
MatchedMethod activate = matches.getBest(ActivateMatcher.class, path, ActionName.ACTIVATE,
|
||||
ActivateMatcher.makeBySpecificity(obj.getRoot().getSchema(), path));
|
||||
if (activate == null) {
|
||||
return null;
|
||||
}
|
||||
if (activate.params.get("time") != null) {
|
||||
return ScheduleForm.SNAP_ANY_STEPS_OPS;
|
||||
}
|
||||
if (activate.params.get("snap") != null) {
|
||||
return ScheduleForm.SNAP_ONLY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ScheduleForm getSupportedTimeFormByAttribute(TraceObject obj, long snap) {
|
||||
TraceObject eventScope = obj.findSuitableInterface(TraceObjectEventScope.class);
|
||||
if (eventScope == null) {
|
||||
return null;
|
||||
}
|
||||
TraceObjectValue timeSupportStr =
|
||||
eventScope.getAttribute(snap, TraceObjectEventScope.KEY_TIME_SUPPORT);
|
||||
if (timeSupportStr == null) {
|
||||
return null;
|
||||
}
|
||||
return ScheduleForm.valueOf(timeSupportStr.castValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduleForm getSupportedTimeForm(TraceObject obj, long snap) {
|
||||
ScheduleForm byMethod = getSupportedTimeFormByMethod(obj);
|
||||
if (byMethod == null) {
|
||||
return null;
|
||||
}
|
||||
ScheduleForm byAttr = getSupportedTimeFormByAttribute(obj, snap);
|
||||
if (byAttr == null) {
|
||||
return null;
|
||||
}
|
||||
return byMethod.intersect(byAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceExecutionState getThreadExecutionState(TraceThread thread) {
|
||||
if (!(thread instanceof TraceObjectThread tot)) {
|
||||
|
@ -385,7 +427,8 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
.orElse(null);
|
||||
}
|
||||
|
||||
record ParamAndObjectArg(RemoteParameter param, TraceObject obj) {}
|
||||
record ParamAndObjectArg(RemoteParameter param, TraceObject obj) {
|
||||
}
|
||||
|
||||
protected ParamAndObjectArg getFirstObjectArgument(RemoteMethod method,
|
||||
Map<String, Object> args) {
|
||||
|
@ -828,7 +871,8 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
static final List<ToggleBreakMatcher> SPEC = matchers(HAS_SPEC);
|
||||
}
|
||||
|
||||
record MatchKey(Class<? extends MethodMatcher> cls, ActionName action, TraceObjectSchema sch) {}
|
||||
record MatchKey(Class<? extends MethodMatcher> cls, ActionName action, TraceObjectSchema sch) {
|
||||
}
|
||||
|
||||
protected class Matches {
|
||||
private final Map<MatchKey, MatchedMethod> map = new HashMap<>();
|
||||
|
@ -975,7 +1019,8 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
@Override
|
||||
public CompletableFuture<Void> activateAsync(DebuggerCoordinates prev,
|
||||
DebuggerCoordinates coords) {
|
||||
if (prev.getSnap() != coords.getSnap()) {
|
||||
boolean timeNeq = !Objects.equals(prev.getTime(), coords.getTime());
|
||||
if (timeNeq) {
|
||||
requestCaches.invalidate();
|
||||
}
|
||||
TraceObject object = coords.getObject();
|
||||
|
@ -983,10 +1028,9 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
return AsyncUtils.nil();
|
||||
}
|
||||
|
||||
MatchedMethod activate =
|
||||
matches.getBest(ActivateMatcher.class, object.getCanonicalPath(), ActionName.ACTIVATE,
|
||||
() -> ActivateMatcher.makeBySpecificity(trace.getObjectManager().getRootSchema(),
|
||||
object.getCanonicalPath()));
|
||||
KeyPath path = object.getCanonicalPath();
|
||||
MatchedMethod activate = matches.getBest(ActivateMatcher.class, path, ActionName.ACTIVATE,
|
||||
ActivateMatcher.makeBySpecificity(object.getRoot().getSchema(), path));
|
||||
if (activate == null) {
|
||||
return AsyncUtils.nil();
|
||||
}
|
||||
|
@ -996,11 +1040,11 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
args.put(paramFocus.name(),
|
||||
object.findSuitableSchema(getSchemaContext().getSchema(paramFocus.type())));
|
||||
RemoteParameter paramTime = activate.params.get("time");
|
||||
if (paramTime != null) {
|
||||
if (paramTime != null && (paramTime.required() || timeNeq)) {
|
||||
args.put(paramTime.name(), coords.getTime().toString());
|
||||
}
|
||||
RemoteParameter paramSnap = activate.params.get("snap");
|
||||
if (paramSnap != null) {
|
||||
if (paramSnap != null && (paramSnap.required() || timeNeq)) {
|
||||
args.put(paramSnap.name(), coords.getSnap());
|
||||
}
|
||||
return activate.method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null);
|
||||
|
|
|
@ -56,6 +56,10 @@ message Snap {
|
|||
int64 snap = 1;
|
||||
}
|
||||
|
||||
message Schedule {
|
||||
string schedule = 1;
|
||||
}
|
||||
|
||||
message Span {
|
||||
int64 min = 1;
|
||||
int64 max = 2;
|
||||
|
@ -392,10 +396,14 @@ message RequestSnapshot {
|
|||
DomObjId oid = 1;
|
||||
string description = 2;
|
||||
string datetime = 3;
|
||||
Snap snap = 4;
|
||||
oneof time {
|
||||
Snap snap = 4;
|
||||
Schedule schedule = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message ReplySnapshot {
|
||||
Snap snap = 1;
|
||||
}
|
||||
|
||||
// Client commands
|
||||
|
|
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||
|
||||
[project]
|
||||
name = "ghidratrace"
|
||||
version = "11.3"
|
||||
version = "11.4"
|
||||
authors = [
|
||||
{ name="Ghidra Development Team" },
|
||||
]
|
||||
|
@ -23,3 +23,6 @@ dependencies = [
|
|||
[project.urls]
|
||||
"Homepage" = "https://github.com/NationalSecurityAgency/ghidra"
|
||||
"Bug Tracker" = "https://github.com/NationalSecurityAgency/ghidra/issues"
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
ghidratrace = ["py.typed"]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,114 @@
|
|||
## ###
|
||||
# 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 concurrent.futures import Future
|
||||
from typing import Any, Callable, List, Optional, Sequence, TypeVar, Union
|
||||
from .client import Address, TraceObject, TraceObjectValue
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
def wait_opt(val: Union[T, Future[T], None]) -> Optional[T]:
|
||||
if val is None:
|
||||
return None
|
||||
if isinstance(val, Future):
|
||||
return val.result()
|
||||
return val
|
||||
|
||||
|
||||
def wait(val: Union[T, Future[T]]) -> T:
|
||||
if isinstance(val, Future):
|
||||
return val.result()
|
||||
return val
|
||||
|
||||
|
||||
class TableColumn(object):
|
||||
def __init__(self, head: str) -> None:
|
||||
self.head = head
|
||||
self.contents = [head]
|
||||
self.is_last = False
|
||||
|
||||
def add_data(self, data: str) -> None:
|
||||
self.contents.append(data)
|
||||
|
||||
def finish(self) -> None:
|
||||
self.width = max(len(d) for d in self.contents) + 1
|
||||
|
||||
def format_cell(self, i: int) -> str:
|
||||
return (self.contents[i] if self.is_last
|
||||
else self.contents[i].ljust(self.width))
|
||||
|
||||
|
||||
class Tabular(object):
|
||||
def __init__(self, heads: List[str]) -> None:
|
||||
self.columns = [TableColumn(h) for h in heads]
|
||||
self.columns[-1].is_last = True
|
||||
self.num_rows = 1
|
||||
|
||||
def add_row(self, datas: List[str]) -> None:
|
||||
for c, d in zip(self.columns, datas):
|
||||
c.add_data(d)
|
||||
self.num_rows += 1
|
||||
|
||||
def print_table(self, println: Callable[[str], None]) -> None:
|
||||
for c in self.columns:
|
||||
c.finish()
|
||||
for rn in range(self.num_rows):
|
||||
println(''.join(c.format_cell(rn) for c in self.columns))
|
||||
|
||||
|
||||
def repr_or_future(val: Union[T, Future[T]]) -> str:
|
||||
if isinstance(val, Future):
|
||||
if val.done():
|
||||
return str(val.result())
|
||||
else:
|
||||
return "<Future>"
|
||||
else:
|
||||
return str(val)
|
||||
|
||||
|
||||
def obj_repr(obj: TraceObject) -> str:
|
||||
if obj.path is None:
|
||||
if obj.id is None:
|
||||
return "<ERR: no path nor id>"
|
||||
else:
|
||||
return f"<id={repr_or_future(obj.id)}>"
|
||||
elif isinstance(obj.path, Future):
|
||||
if obj.path.done():
|
||||
return obj.path.result()
|
||||
elif obj.id is None:
|
||||
return "<path=<Future>>"
|
||||
else:
|
||||
return f"<id={repr_or_future(obj.id)}>"
|
||||
else:
|
||||
return obj.path
|
||||
|
||||
|
||||
def val_repr(value: Any) -> str:
|
||||
if isinstance(value, TraceObject):
|
||||
return obj_repr(value)
|
||||
elif isinstance(value, Address):
|
||||
return f'{value.space}:{value.offset:08x}'
|
||||
return repr(value)
|
||||
|
||||
|
||||
def print_tabular_values(values: Sequence[TraceObjectValue],
|
||||
println: Callable[[str], None]) -> None:
|
||||
table = Tabular(['Parent', 'Key', 'Span', 'Value', 'Type'])
|
||||
for v in values:
|
||||
table.add_row([obj_repr(v.parent), v.key, str(v.span),
|
||||
val_repr(v.value), str(v.schema)])
|
||||
table.print_table(println)
|
|
@ -1,22 +1,21 @@
|
|||
## ###
|
||||
# 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 dataclasses import dataclass
|
||||
|
||||
|
||||
# Use instances as type annotations or as schema
|
||||
@dataclass(frozen=True)
|
||||
class Schema:
|
||||
name: str
|
||||
|
@ -25,6 +24,7 @@ class Schema:
|
|||
return self.name
|
||||
|
||||
|
||||
UNSPECIFIED = Schema('')
|
||||
ANY = Schema('ANY')
|
||||
OBJECT = Schema('OBJECT')
|
||||
VOID = Schema('VOID')
|
||||
|
|
|
@ -1,33 +1,38 @@
|
|||
## ###
|
||||
# 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 concurrent.futures import Future
|
||||
import socket
|
||||
import traceback
|
||||
from typing import TypeVar
|
||||
|
||||
from google.protobuf import message as _message
|
||||
|
||||
M = TypeVar('M', bound=_message.Message)
|
||||
|
||||
|
||||
def send_length(s, value):
|
||||
def send_length(s: socket.socket, value: int) -> None:
|
||||
s.sendall(value.to_bytes(4, 'big'))
|
||||
|
||||
|
||||
def send_delimited(s, msg):
|
||||
def send_delimited(s: socket.socket, msg: _message.Message) -> None:
|
||||
data = msg.SerializeToString()
|
||||
send_length(s, len(data))
|
||||
s.sendall(data)
|
||||
|
||||
|
||||
def recv_all(s, size):
|
||||
def recv_all(s, size: int) -> bytes:
|
||||
buf = b''
|
||||
while len(buf) < size:
|
||||
part = s.recv(size - len(buf))
|
||||
|
@ -38,14 +43,14 @@ def recv_all(s, size):
|
|||
# return s.recv(size, socket.MSG_WAITALL)
|
||||
|
||||
|
||||
def recv_length(s):
|
||||
def recv_length(s: socket.socket) -> int:
|
||||
buf = recv_all(s, 4)
|
||||
if len(buf) < 4:
|
||||
raise Exception("Socket closed")
|
||||
return int.from_bytes(buf, 'big')
|
||||
|
||||
|
||||
def recv_delimited(s, msg, dbg_seq):
|
||||
def recv_delimited(s: socket.socket, msg: M, dbg_seq: int) -> M:
|
||||
size = recv_length(s)
|
||||
buf = recv_all(s, size)
|
||||
if len(buf) < size:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue